【问题标题】:Printing a string to a temporary stream object in C++在 C++ 中将字符串打印到临时流对象
【发布时间】:2011-11-04 18:10:51
【问题描述】:

我有一种特殊类型的 ostringstream,我试图将文本作为临时对象输出到,但我遇到了一些麻烦。需要明确的是,这基本上是我想要做的:

ostringstream() << "PARTY DOWN!" << endl;

现在在你说:“但是 Zack,那个代码完全没有价值!对象在行尾被销毁,你怎么知道它是否做了什么?”,听我说完。我不会尝试使用普通的 ostringstreams 来执行此操作,而是使用派生类,其中析构函数实际上为数据提供了退出对象的路径。所以实际上,它看起来更像这样:

specialstringstream() << "PARTY DOWN!" << endl;

specialstringstream 有一个析构函数,可以将文本转储到别处。

我不会详细说明我为什么这样做。您必须相信我,它对我需要做的事情很有意义,并且非常适合现有的庞大代码库。

问题是:当我这样做时,一切都编译并运行,但我得到一个指针地址打印到我的输出而不是“PARTY DOWN!”细绳。我确定这是因为编译器选择执行流输出的运算符是 ostream&amp; operator&lt;&lt; (const void* val),不是ostream&amp; operator&lt;&lt; (ostream&amp; out, const char* s )

我对为什么有一个模糊的想法,但我不知道如何解决它。我该怎么做才能让 char*s 打印到字符串流的临时实例中?

下面是展示该行为的 SpecialStringStream 对象的简短版本:

class SpecialStringStream : public ostringstream
{
  public:
    SpecialStringStream(ostream* regularStream)
    {
      regularStream_ = regularStream;
    }

    ~SpecialStringStream()
    {
      if (regularStream_ != NULL)
        (*regularStream_) << str(); 
    }

  private:
    ostream* regularStream_;
};

当我执行以下操作时:SpecialStringStream(someStreamPtr) &lt;&lt; "PARTY DOWN!" &lt;&lt; endl;,我在输出中得到一个指针地址,如“00444D60”而不是消息。

编辑:由于我对用户太陌生,无法回答自己的问题,因此感谢所有回复,这就是我所决定的。

我想出了以下解决方案,它适用于 Visual C++ 8 和我需要的所有其他编译器。我创建了一个模板运算符,它基本上去除了 const SpecialStringStream 的常量,将其转换为 ostream,并让 ostream 运算符完成他们的工作。随意在 cmets 中将其撕成碎片,并警告我我引入的所有可怕的潜在错误!

template <class T>
std::ostream& operator<<(const SpecialStringStream &o, T msg)
{
    return static_cast<std::ostream&>(const_cast<SpecialStringStream&>(o)) << msg;
}

【问题讨论】:

  • 我怀疑是不是这个原因,因为如果是这样,那么cout &lt;&lt; "hi" 将不起作用,因为它会“选择ostream&amp; operator&lt;&lt;(const void* val) 而不是ostream&amp; operator&lt;&lt;(const char* val)”。
  • 请提供更多代码.. 特别是您的特殊字符串实现
  • 我们能否请您查看展示该行为的 SpecialStringStream 的最小完整实现? (此外,这不是一个不寻常的习惯用法——您可以轻松想象,例如,SomeLogger() &lt;&lt; "error code: " &lt;&lt; ec; 在临时对象销毁时廉价地刷新日志缓冲区。)
  • 我确定您的 specialstringstream() 实现在某些方面是错误的...请粘贴代码,这很有趣!

标签: c++ object reference constants temporary


【解决方案1】:

重载ostream&amp; operator&lt;&lt; (ostream&amp; out, const char*) 不可行,因为您的临时不会绑定到非常量引用ostream&amp;。除了声明一个局部变量并使用它之外,您实际上无能为力(因为您不能将右值转换为左值):

{
  specialstringstream ss;
  ss << "Hello world" << std::endl; // OK, can bind to lvalue
}

可能的解决方案:您可以声明另一个接受右值引用的重载:

std::ostream & operator<<(specialstringstream && o, const char * s)
{
  return o << s; // note: *not* "std::move(o)"
}

【讨论】:

  • 再次精彩!我从来没有想过。
  • @LokiAstari:我从来没有这么说。这是关于右值与左值的。在您的示例中,std::cout 是一个左值。
  • 虽然这确实有效并且我感谢您的帮助,但我应该在我的问题中提到我正在寻找一种解决方法来让代码工作而不改变现有代码库执行的每个地方输出。或许通过向特殊类添加运算符重载。
  • 您可以将运算符更改为std::ostream &amp; operator&lt;&lt;(const std::ostream &amp; o, const char * s) 并使用const_cast。但是,如果您滥用它,那将是UB。或者,声明 another 重载 std::ostream &amp; operator&lt;&lt;(std::ostream &amp;&amp; o, const char * s) 以处理 rvlaues。
  • 唉,我需要这段代码在 VC8 中编译。在那种环境下,我似乎无法使用任何一种解决方案。不支持右值引用,它声称:“'const_cast':无法从'const ostream'转换为'ostream'。转换需要构造函数或用户定义的转换运算符,const_cast或reinterpret_cast不能使用。 "我认为我必须做的是确保我在这里用我的新类实现的功能在旧编译器上关闭。
【解决方案2】:

您不想实现字符串流。您想实现一个写入您的特殊字符串的 basic_streambuf。

流本身负责格式化和类似的功能;流缓冲区负责最终成为数据的接收器。

毕竟,一个stringstream就是一个iostream,附加了一个basic_stringbuf。

【讨论】:

    【解决方案3】:

    也许有更好的方法,但我想出了另一种解决方案:

    #include <iostream>
    #include <sstream>
    
    class LogClass
    { 
        template <typename T>
        friend const LogClass& operator << (const LogClass& lc, const T& v);
    public:
        LogClass()
            : str(new std::ostringstream())
            , refCount(new int(1))
        {}
    
        LogClass(const LogClass& other)
            : str(other.str)
        {
            ++(*refCount);
        }
    
        ~LogClass()
        {
            --(*refCount);
            if (!*refCount)
            {
                delete refCount;
                std::cout << str->str() << std::endl;
                delete str;
            }
        }
    private:
        mutable std::ostringstream *str;
        int *refCount;
    
        LogClass& operator = (const LogClass&);
    };
    
    template <typename T>
    const LogClass& operator << (const LogClass& lc, const T& v)
    {
        (*lc.str) << v;
        return lc;
    }
    
    int main(int , char**)
    {
        for (size_t i = 0; i < 10 ; ++i)
        {
            LogClass() << "PARTY DOWN! " << i;
        }
    }
    

    用 valgrind 运行它:

    valgrind --tool=memcheck --leak-check=full ./LogClass

    ==16197== Memcheck,内存错误检测器

    ==16197== 版权所有 (C) 2002-2010 和 GNU GPL,由 Julian Seward 等人撰写。

    ==16197== 使用 Valgrind-3.7.0.SVN 和 LibVEX;使用 -h 重新运行以获取版权信息

    ==16197== 命令:./LogClass

    ==16197==

    派对结束! 0

    派对结束! 1

    派对结束! 2

    派对结束! 3

    派对结束! 4

    派对结束! 5

    派对结束! 6

    派对结束! 7

    派对结束! 8

    派对结束! 9

    ==16197==

    ==16197== 堆摘要:

    ==16197== 在退出时使用:0 个块中的 0 个字节

    ==16197== 总堆使用量:40 次分配,40 次释放,7,350 字节分配

    ==16197==

    ==16197== 所有堆块都被释放——不可能有泄漏

    ==16197==

    ==16197== 对于检测到和抑制的错误计数,重新运行:-v

    ==16197== 错误摘要:0 个上下文中的 0 个错误(已抑制:8 个中的 15 个)

    这就是我想要的,但它不是线程安全的。使用 boost 中的 shared_ptr 来实现。

    【讨论】:

      【解决方案4】:

      这是我正在使用的解决方法:

      #define STRM2STR(x) (dynamic_cast<std::ostringstream &>(std::ostringstream() << std::dec << x).str())
      

      插入 std::dec 将导致调用 ostream::operator

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-08
        • 1970-01-01
        • 2012-09-04
        相关资源
        最近更新 更多