Re: Bad use of stringstream temporary?
On 3/24/2011 4:18 PM, K. Frank wrote:
Hello Group!
First off, thanks to all who responded.
On Mar 24, 3:13 pm, Jeff Flinn<TriumphSprint2...@hotmail.com> wrote:
Victor Bazarov wrote:
On 3/24/2011 12:08 PM, K. Frank wrote:
The basic question is whether the following line of code
is legal and good (according to the current standard):
std::string s1 =
dynamic_cast<std::stringstream&>(std::stringstream()<< "abc").str();
The problem is that "abc" gets rendered as a hex pointer
value, rather than as "abc".
...
The only function possible is the member function operator<< with the
argument that is void*. The non-member operator<<(stream&, const char*)
cannot be used because there is no binding of a temporary to a reference
to non-const.
The behavior is as expected.
Yes, this explanation seems to be the consensus. As I understand it,
the code is perfectly legal, but, because of the temporary
stringstream,
operator<< resolves to the member function with argument void*, hence
printing out the pointer value.
I believe I understand what everyone is saying, and why this is the
behavior specified by the standard.
But there are still some details I don't understand...
...
IIRC,
std::string s = (std::ostringstream()<< std::flush<< "abc").string();
behaves as the OP expected, at least on MSVC8 and gcc4.0.1 under XCode
3.1.2.
As Jeff says, adding std::flush causes the code to work as I had
expected, namely by printing out "abc".
My variant of the code is:
std::string s =
dynamic_cast<std::stringstream&>(std::stringstream()<< std::flush<<
"abc").str();
Note, inserting other stuff in place of std::flush has the same
effect:
std::string s =
dynamic_cast<std::stringstream&>(std::stringstream()<< std::skipws<<
"abc").str();
prints out "abc".
std::string s =
dynamic_cast<std::stringstream&>(std::stringstream()<< 123<<
"abc").str();
prints out "123abc".
std::string s =
dynamic_cast<std::stringstream&>(std::stringstream()<< "xyz"<<
"abc").str();
prints out<pointer value>"abc".
(I tested these specifically with mingw g++ 4.4.1.)
I'm not sure exactly how the manipulators are processed, but as I
understand it, when first inserting 123 or "xyz", the member-function
operator<< is called, and then returns (*this). (*this) is still
the unnamed temporary, so the second insertion (<< "abc") should
again resolve to the member-function operator<< for void*, and so
should again print out the pointer value rather than "abc".
No. Unnamed temporary it is, but the call to the first member (to
output the number or the pointer) returns a reference to non-const,
which then is passed directly to the non-member. There is no binding of
a reference to a temporary after the first call.
Example:
struct A {
A& foo();
};
A& bar(A&);
int main() {
A().foo(); // OK
bar(A().foo()); // OK
bar(A()); // not OK - attempt to bind a non-const ref to a temp
}
It's as if the first insertion operator causes the fact that we're
processing an unnamed temporary to be forgotten, and the second
insertion now resolves (incorrectly?) to the free-function operator<<
for char*, and prints out "abc" rather than the pointer value.
No. The temporary is an object. It's temporary, but not constant. The
language rules require that when a reference is initialized from a
temporary, the reference has to be to a const object. But since the
temporary object is non-const, a non-const member function is allowed to
be called for it. That function can return a reference to non-const,
and you can initialize another non-const ref with that ref, and so on,
and use the object (and change it) *as long as* the temporary is still
*alive*.
Is this now a compiler error, or is there another layer of explanation
that makes this the correct behavior?
That's correct behaviour. You can call it a loophole in the language.
Jeff
Thanks Jeff for pointing out the std::flush variation, and thanks to
all
for any further insight.
K. Frank
V
--
I do not respond to top-posted replies, please don't ask