在 stringstream 中流式传输字符串流 代码针对 GCC 编译,直到 V5.5 版本,但以下版本不编译

Streaming a stringstream within a stringstream Code compiles for GCC until V5.5 but not the following versions

提问人:Bilal Ahmed 提问时间:5/30/2023 最后编辑:Bilal Ahmed 更新时间:5/31/2023 访问量:69

问:

我有以下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

C++ GCC 字符串流

评论

1赞 πάντα ῥεῖ 5/30/2023
首先请使用正确的头文件!
1赞 molbdnilo 5/30/2023
AFAIK,这从来都不是任何标准的一部分。旧的 g++ 版本可能实现了它(标准只是最低限度)并且它被删除了。
0赞 Öö Tiib 5/30/2023
任何扩展的问题是,在赞助这些扩展的人决定停止这些扩展后,可以删除这些扩展。您希望该代码调用什么重载<<?从列表中分辨数字:<en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt>
0赞 Aconcagua 5/30/2023
@ÖöTiib 不过,这还不完整,请参阅 en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2
1赞 Öö Tiib 5/30/2023
@BilalAhmed所以你应该添加自己的重载......像 Record& 运算符<<(const std::stringstream& m) ...如果你只想输出 m.str(),它将在其他编译器和更现代的 GCC 上编译,这通常是有益的。

答:

1赞 Turtlefight 5/31/2023 #1

这不取决于 gcc 版本,而是取决于您正在编译的 C++ 标准版本。

在编译 C++03 时,您的示例在最新的 gcc 版本上没有问题:godbolt


这完全编译的原因是 C++11 之前没有数据类型。
因此,检查流是否有效(例如)被要求只返回一些未指定的类似布尔的类型:
boolif(stream) { /*...*/ }

cppreference

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

请注意,转换在 C++11 之前也是隐式的,因为显式转换函数是在 C++11 中添加的。

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

请注意,这允许从任何流到 .void*

这很不幸,因为它为输出指针提供了重载:
cppreference
std::basic_ostream

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++ 可以通过传递底层流缓冲区而不是整个流来写入,例如:
godbolt
std::istreamstd::ostreams

#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();