The code below demonstrates that when streaming the return-value from std::stringstream::operator<< to a std::ostream instance, like std::cout, an explicit static_cast (from std::basic_ostream<char> back to std::stringstream) is necessary with g++ 9.4.0 and causes a compile error with g++ 11.3:
// main.cpp
#include <iostream>
#include <sstream>
#define LOG(x) \
std::cout << (static_cast<std::stringstream&>(std::stringstream{} << x).str()) << std::endl;
//std::cout << ((std::stringstream{} << x).str()) << std::endl;
int main(int argc, char* argv[]) {
LOG("hello, world!");
return 0;
}
$ g++ --version && g++ ./main.cpp && ./a.out
g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
hello, world!
$
$ g++ --version && g++ -g ./main.cpp && ./a.out
g++ (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
./main.cpp: In function ‘int main(int, char**)’:
./main.cpp:7:15: error: invalid ‘static_cast’ from type ‘std::__cxx11::basic_stringstream<char>’ to type ‘std::stringstream&’ {aka ‘std::__cxx11::basic_stringstream<char>&’}
7 | std::cout << (static_cast<std::stringstream&>(std::stringstream{} << x).str()) << std::endl;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./main.cpp:11:3: note: in expansion of macro ‘LOG’
11 | LOG("hello, world!");
| ^~~
$
And vice-versa: the lack of an explicit static_cast causes g++ 9.4.0 to emit an error and succeeds with g++ 11.3:
// main.cpp
#include <iostream>
#include <sstream>
#define LOG(x) \
std::cout << ((std::stringstream{} << x).str()) << std::endl;
//std::cout << (static_cast<std::stringstream&>(std::stringstream{} << x).str()) << std::endl;
int main(int argc, char* argv[]) {
LOG("hello, world!");
return 0;
}
$ g++ --version && g++ ./main.cpp && ./a.out
g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
./main.cpp: In function ‘int main(int, char**)’:
./main.cpp:7:42: error: ‘class std::basic_ostream<char>’ has no member named ‘str’
7 | std::cout << ((std::stringstream{} << x).str()) << std::endl;
| ^~~
./main.cpp:11:3: note: in expansion of macro ‘LOG’
11 | LOG("hello, world!");
| ^~~
$
$ g++ --version && g++ -g ./main.cpp && ./a.out
g++ (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
hello, world!
$
This is a source of annoyance, because I need to compile this code in two environments: one with g++ 9.4.0 and the other with g++ 11.3.
Please help me understand the nature of these compile errors, specifically wrt the discrepancy between g++ 9.4.0 and g++ 11.3:
- Which compiler version is in the right?
- Is there a preprocessor macro to test the compiler version, by which I can conditionalize the definition of
LOG(x)?
(I would have assumed that g++ 11.3 is correct since it is more recent; but this leaves me wondering: why isn't the explicit static_cast necessary? According to https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt, the std::stringstream insertion operator does indeed return a reference to base-class basic_ostream, which has no str() function, so I'm puzzled why std::cout << ((std::stringstream{} << x).str()) << std::endl; is valid)
Try
Because (std::stringstream{} << x) is not a lvalue.
In fact, clang++ give a better error message: