在stringstream Code中对stringstream进行流式处理可以为GCC编译到V5.5,但不包括以下版本

qoefvg9y  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(253)

我有下面的C++代码,可以成功编译gcc < v5.5

#include <sstream>

namespace util
{

   inline void operator << (std::stringstream& stream, const char* data)
   {
      data = data ? data : "(null)";
      std::operator<<(stream, data);
   }
}

class Record
{
public:

   template<typename T>
   Record& operator<<(const T& data)
   {
      using namespace util;
      m_message << data;
    //   std::cout << data << std::endl;
      return *this;
   }
   
private:
   std::stringstream m_message;
};

int main()
{   
    Record r;
    std::stringstream m;
    r << m; //Error after gcc5.5
    r << m.str(); //Ok in both
    return 0;
    
}

看起来将字符串流传递给字符串流不再有效。我不知道为什么
这里有一个比较的链接:https://godbolt.org/z/novWE3so8

a64a0gku

a64a0gku1#

这并不取决于gcc版本,而是取决于您正在编译的C标准版本。
你的例子在C
03下编译时,使用最新的gcc版本没有问题:godbolt
之所以能编译,是因为在C++11之前没有bool数据类型。
检查流是否有效(例如if(stream) { /*...*/ })因此被强制只返回一些未指定的类似于布尔的类型:
cppreference

operator /* unspecified-boolean-type */() const; // (until C++11)
explicit operator bool() const; // (since C++11)

请注意,在C11之前,转换也是隐式的,因为explicit conversion functions是在C11中添加的。
libstdc++恰好定义了转换操作符如下:GitHub

///@{
/**
 *  @brief  The quick-and-easy status check.
 *
 *  This allows you to write constructs such as
 *  <code>if (!a_stream) ...</code> and <code>while (a_stream) ...</code>
 */
#if __cplusplus >= 201103L
      explicit operator bool() const { return !this->fail(); }
#else
      operator void*() const { return this->fail() ? 0 : const_cast<basic_ios*>(this); }
#endif

注意,这允许使用 implicit(!)从任何流到void*的转换。
这是不幸的,因为std::basic_ostream提供了输出指针的重载:
cppreference

basic_ostream& operator<<( const void* value ); // (8)

因此,在您的示例中实际发生的情况是,流被隐式转换为void*指针,然后调用operator<<(const void*)以输出该指针。
这可能不是你想要的结果
godbolt

#include <iostream>
#include <sstream>

int main() {
    std::stringstream m;
    m << "foobar";

    std::stringstream ss;
    // equivalent to  ss << static_cast<void*>(&m);
    ss << m;

    std::cout << ss.str() << std::endl;
    // will print the address of m, e.g. 0x7ffcda4c2540
    // NOT foobar!
    return 0;
}
追加流的正确方法

C++ std::istream s可以通过传递底层流缓冲区而不是整个流来写入std::ostreams,例如:
godbolt

#include <iostream>
#include <sstream>

int main() {
    std::stringstream m;
    m << "foobar";

    std::stringstream ss;
    ss << m.rdbuf(); // append everything from m to ss

    std::cout << ss.str() << std::endl;
    return 0;
}

所以你的例子可以这样修改:

// r << m;
r << m.rdbuf();

相关问题