In C++, is it possible to call a function whose argument is std::stringstream& without the caller declaring an explicit named variable of type std::stringstream?
I.e. I would like to accomplish something along the lines of this:
#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>
void func(const std::stringstream& ss) {
std::cout << ss.str() << std::endl;
}
int main() {
func("Hello World " << errno << "(" << strerror(errno) << ")");
return 0;
}
The above code generates this compile error:
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.c: In function ‘int main()’:
./main.c:11:23: error: invalid operands of types ‘const char [13]’ and ‘int’ to binary ‘operator<<’
11 | func("Hello World " << errno << "(" << strerror(errno) << ")");
| ~~~~~~~~~~~~~~ ^~
| |
| const char [13]
(I’m not sure if I properly understand the problem — I think it is because the const char*
"Hello World" is not implicitly cast to anything for which the <<
operator is applicable — is that correct?)
I tried this next:
#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>
void func(const std::stringstream& ss) {
std::cout << ss.str() << std::endl;
}
int main() {
func(std::stringstream("Hello World ") << errno << "(" << strerror(errno) << ")");
return 0;
}
…which had an interesting result: on my PC, with g++ I got the following compile error:
$ g++ -g ./main.c && ./a.out
./main.c: In function ‘int main()’:
./main.c:19:77: error: invalid initialization of reference of type ‘const stringstream&’ {aka ‘const std::__cxx11::basic_stringstream<char>&’} from expression of type ‘std::basic_ostream<char>’
19 | func(std::stringstream("Hello World ") << errno << "(" << strerror(errno) << ")");
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
./main.c:6:36: note: in passing argument 1 of ‘void func(const stringstream&)’
6 | void func(const std::stringstream& ss) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~^~
If I understand the compile error correctly, the value being passed to func()
is of type std::basic_ostream<char>
which cannot be implicitly cast to std::stringstream
. Is this because the return-value of operator<<
is type std::basic_ostream
? (https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt)
The interesting part, though, is that at randomly-chosen online C++ compiler https://www.programiz.com/cpp-programming/online-compiler/, the same code compiles, however what gets printed to stdout is:
0(Success)d
…which seems to show the string "Hello World" being overwritten by the the value of errno << "(" << strerror(errno) << ")"
I presume this difference of behavior is due to different compilers, and I would like to understand if/why this is the case — but this is tangential to the specific question asked here, and I may ask it as a separate, specific question.
tl;dr: in the following code, function main()
invokes function func()
by using an explicit local, named variable:
#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>
void func(const std::stringstream& ss) {
std::cout << ss.str() << std::endl;
}
int main() {
std::stringstream ss;
ss << "Hello World " << errno << "(" << strerror(errno) << ")";
func(ss);
return 0;
}
$ g++ -g ./main.c && ./a.out
Hello World 0(Success)
…is there a way to invoke function func()
without the the caller using an explicit, named variable?
Update: I tried the following based on an answer-as-comment:
#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>
void func(const std::stringstream& ss) {
std::cout << ss.str() << std::endl;
}
int main() {
func(std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")");
return 0;
}
…which resulted in this compile-error:
$ g++ -g ./main.c && ./a.out
./main.c: In function ‘int main()’:
./main.c:46:81: error: invalid initialization of reference of type ‘const stringstream&’ {aka ‘const std::__cxx11::basic_stringstream<char>&’} from expression of type ‘std::basic_ostream<char>’
46 | func(std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")");
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
./main.c:6:36: note: in passing argument 1 of ‘void func(const stringstream&)’
6 | void func(const std::stringstream& ss) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~^~
…which looks the same as when I tried func(std::stringstream("Hello World ") << errno << "(" << strerror(errno) << ")");
Update: I tried the following based on an answer-as-comment:
#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>
void func(const std::stringstream& ss) {
std::cout << ss.str() << std::endl;
}
int main() {
func((std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")").str());
return 0;
}
…which resulted in this compile error:
$ g++ -g ./main.c && ./a.out
./main.c: In function ‘int main()’:
./main.c:11:90: error: ‘class std::basic_ostream<char>’ has no member named ‘str’
11 | func((std::stringstream{} << "Hello World " << errno << "(" << strerror(errno) << ")").str());
|
3
Answers
One possibility would be to use a string instead of a stringstream:
…or possibly, if you don’t mind one extra line elsewhere:
Either way, you would need to rewrite
func
to receive anstd::string
(or astd::string const &
) instead of anstd::stringstream
(but given that allfunc
is doing with it is callingstr()
to get a string, that doesn’t seem like it should be a problem).Another possibility would be to rewrite
func
to to be a variadic template:…which produces the expected result:
May be not what you are looking for, but sometimes it used this way:
As a sidnote, C++ has its own mechanisms to handle
errno
, you can try to use it this way:You can define a user defined literal.
https://godbolt.org/z/bn7sq97Wq
As for the compiler error in your second attempt
It does not work until GCC 11.2.
std::basic_stringstream
inheritsstd::basic_ostream
and the returned type of itsstd::basic_ostream<CharT,Traits>::operator<<
isbasic_ostream&
that is not suitable forconst std::stringstream&
. The work around is the modifiedfunc()
:https://godbolt.org/z/szqvrcbW8