在 GCC 而非 MSVC 中对 wstringstream 类型的参数使用流插入时出错

Error when using stream insertion on parameter of type wstringstream in GCC but not MSVC

我有一个 header,我将其放在一起以协助使用 MSVC v141 (Visual Studio 2017) 登录应用程序。由于这些日志记录实用程序很有用,我想将它引入我刚刚开始的一个使用 CMake 和 MinGW/GCC 的项目中。 header 本身应该是独立的,它只是执行一些魔术来抽象出公共信息和流目的地,所以我所要做的就是在我的主循环中调用 InitLogging() 然后调用 LogInfo/LogError/etc,但是当我针对 MinGW(10.3.0 [MSYS2])/GCC(7.5.0 [Ubuntu]) 进行编译时,出现以下错误:

error: invalid initialization of reference of type ‘const wstringstream& {aka const std::__cxx11::basic_stringstream<wchar_t>&}’ from expression of type ‘std::basic_ostream<wchar_t>’
 #define LogInfo(str) { LogMessage(eLogLevel_INFO, std::wstringstream() << LOG_HELPER << str, std::wstringstream() << LOG_HELPER_2);  }
/mnt/d/dev/test_logger/src/main.cpp:9:2: note: in expansion of macro ‘LogInfo’
  LogInfo(L"Test log");

这里是来自header的相关代码:

std::wstring Time2String();

#define LOG_HELPER __FUNCTION__ << L"(): "
#define LOG_HELPER_2 L"[" << Time2String() << "] "

inline void LogMessage(eLogLevel lvl, const std::wstringstream& str, const std::wstringstream& logHelper);//Snipped definition

#define LogInfo(str) { LogMessage(eLogLevel_INFO, std::wstringstream() << LOG_HELPER << str, std::wstringstream() << LOG_HELPER_2);  }

如果我只是传递 std::wstreamstring()s 而没有任何插入,或者如果我在调用之外实例化该类型的变量并在调用之外对这些变量执行插入,那么编译器会很高兴,尽管它如果我尝试对类似于当前 #define.

的函数调用中的变量执行插入操作,我同样不高兴

示例:

LogInfo(L"Test log");//compile error

/*******************************************/
std::wstringstream tmpSStream, tmpSStream2;
tmpSStream << LOG_HELPER << L"Test log";
tmpSStream2 << LOG_HELPER_2;
LogMessage(eLogLevel_INFO, tmpSStream, tmpSStream2);//no error

/*******************************************/
LogMessage(eLogLevel_INFO, std::wstringstream(), std::wstringstream());//no error

/*******************************************/
std::wstringstream tmpSStream, tmpSStream2;
LogMessage(eLogLevel_INFO, tmpSStream << LOG_HELPER << L"Test log", tmpSStream2 << LOG_HELPER_2);//compile error

这是 GCC 中的错误还是 MSVC 做了不该做的事情?

operator<< returns 对调用它的 std::basic_ostream 对象的引用,即(在本例中)它 returns std::wostream&,而不是 std::wstringstream& 就像你期待的那样。您无法从 std::wostream& 初始化 const std::wstringstring&,因此出现错误消息。

由于您的 LogInfo() 宏正在创建一个带有 {} 大括号的新范围,您可以利用您已经发现的解决方案:

If I ... instantiate variables of that type outside of the call and perform the insertions on those variables outside the call, then the compiler is happy

试试这个:

#define LogInfo(str) { \
    std::wstringstream wss, wss2; \
    wss << LOG_HELPER << str; \
    wss2 << LOG_HELPER_2; \
    LogMessage(eLogLevel_INFO, wss, wss2); \
}

Online Demo

否则,如果您不想使用局部变量,那么您将需要显式地对 operator<< returns 的最终 std::wostream& 引用进行类型转换,例如:

#define LogInfo(str) { \
    LogMessage(eLogLevel_INFO, \
        static_cast<std::wstringstream&>(std::wstringstream() << LOG_HELPER << str), \
        static_cast<std::wstringstream&>(std::wstringstream() << LOG_HELPER_2) \
    ); \
}

Online Demo