Disclaimer: This question is for understanding. I’ll use boost::lexical_cast in the field. It has sort of come up in the real world in places, though.
Take the following attempt at an “inline” lex-cast approach:
#include <string>
#include <sstream>
#include <iostream>
int main()
{
const std::string s = static_cast<std::ostringstream&>(
std::ostringstream() << "hi" << 0
).str();
std::cout << s;
}
The result is something like 0x804947c0, because the operator<< that works with "hi" is a free function whose LHS must take std::ostream&†, and temporary std::ostringstream() can’t bind to a ref-to-non-const. The only remaining match is the operator<< that takes const void* on the RHS††.
#include <string>
#include <sstream>
#include <iostream>
int main()
{
const std::string s = static_cast<std::ostringstream&>(
std::ostringstream() << 0 << "hi"
).str();
std::cout << s;
}
The result is "0hi".
This mostly makes sense, because the operator<< that takes int is a member function of base ostream††† and, as such, is fine with being invoked on the temporary. The result of that operation is a reference to the ostream base, to which the next operator<< is chained, i.e. read it as (std::ostringstream() << 0) << "hi".
But why then does that operation on "hi" go on to yield the expected result? Isn’t the reference on the LHS still a temporary?
Let’s focus on C++03; I’m told that the first example may actually work as “intended” in C++11 due to the catch-all operator for rvalues.
† [C++03: 27.6.2.1]: template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,charT*);
†† [C++03: 27.6.2.1]: basic_ostream<charT,traits>& operator<<(const void* p);
††† [C++03: 27.6.2.1]: basic_ostream<charT,traits>& operator<<(int n);
The reason is simple. If you read the question I asked about:
you will note that the trick to getting a “proper” reference instead of a temporary is to call a method on the object (not restricted to the not binding restriction for some reason) that will return a reference.
In Nawaz’s answer above, he called
std::ostream& std::ostream::flush(), in your case here:you call
std::ostringstream& std::ostringstream::operator<<(int).Same result.
The surprising behavior is due to
ostreammishmash implementations: someoperator<<are member methods while others are free-functions.You can test it, simply, by implementing an
X& ref()method on an object:EDIT: but why is not
X&(the result ofref) treated the same ?It is a matter of classification. A temporary is a
prvaluewhilst a reference is alvalue. A reference is only allowed to bind to alvalue.Of course since methods can be called on
rvalue(and thusprvalue) and those methods may return a reference to the objects they were called on we can easily bypass the silly (1) a reference is only allowed to bind to alvaluerestriction…(1) it’s also inconsistent with the fact that a
rvaluecan be bound to a const-reference.