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††.
Now let's swap the operands:
#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:
std::ostringstream printing the address of the c-string instead of its content.
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:
std::ostringstream() << 0 << "hi"
you call std::ostringstream& std::ostringstream::operator<<(int)
.
Same result.
The surprising behavior is due to ostream
mishmash implementations: some operator<<
are member methods while others are free-functions.
You can test it, simply, by implementing an X& ref()
method on an object:
struct X { X& ref(); };
void call(X& x);
int main() {
call(X{}); // error: cannot bind X& to a temporary
call(X{}.ref()); // OK
}
EDIT: but why is not X&
(the result of ref
) treated the same ?
It is a matter of classification. A temporary is a prvalue
whilst a reference is a lvalue
. A reference is only allowed to bind to a lvalue
.
Of course since methods can be called on rvalue
(and thus prvalue
) 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 a lvalue
restriction...
(1) it's also inconsistent with the fact that a rvalue
can be bound to a const-reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With