Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

lvalues, rvalues, temporary values - peculiar behaviour

192 views
Skip to first unread message

Simon Parmenter

unread,
Oct 2, 2012, 7:23:01 PM10/2/12
to
Hi All

I was just playing around with some test code I had done sometime ago and
found some behaviour I could not explain; looking up the C++11 standard
did me no good either.

It may be the std::string implementation - just guessing here.
So what do you think?

Platform: Ubuntu 12.04 LTS
Compiler: g++ 4.7.2
g++ cmdline: -ggdb -std=c++11 -Wall -Wextra -pedantic -fvisibility=hidden

Here are the code examples:

int main()
{
std::string s1("Hello");
std::string s2(" World");

s1 + s2 = "Eh?";
std::string & sr = (s1 + s2 = "Eh?");

//cout << sr << endl;

std::string const & sr1(s1 + s2);


cout << (s1 + s2 = "Eh?") << endl;
cout << sr << endl;
cout << sr1 << endl;
}

Output:
Eh?
Hello World
Hello World


int main()
{
std::string s1("Hello");
std::string s2(" World");

s1 + s2 = "Eh?";
std::string & sr = (s1 + s2 = "Eh?");

cout << sr << endl;

std::string const & sr1(s1 + s2);


cout << (s1 + s2 = "Eh?") << endl;
//cout << sr << endl;
cout << sr1 << endl;
}

Output:
<empty line>
Eh?
Hello World


int main()
{
std::string s1("Hello");
std::string s2(" World");

s1 + s2 = "Eh?";
std::string & sr = (s1 + s2 = "Eh?");

cout << sr << endl;

std::string const & sr1(s1 + s2);


cout << (s1 + s2 = "Eh?") << endl;
cout << sr << endl;
cout << sr1 << endl;
}

Output:
<empty line>
Eh?
Hello World
Hello World


int main()
{
std::string s1{"Hello"};
std::string s2{" World"};

s1 + s2 = "Eh?";
std::string const & sr{s1 + s2 = "Eh?"};

cout << sr << endl;

std::string const & sr1{s1 + s2};


cout << (s1 + s2 = "Eh?") << endl;
cout << sr << endl;
cout << sr1 << endl;

}

Output:
Eh?
Eh?
Eh?
Hello World


Simon


--


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Francis Glassborow

unread,
Oct 3, 2012, 12:19:10 PM10/3/12
to
On 03/10/2012 00:23, Simon Parmenter wrote:
> Hi All
>
> I was just playing around with some test code I had done sometime ago and
> found some behaviour I could not explain; looking up the C++11 standard
> did me no good either.
>
> It may be the std::string implementation - just guessing here.
> So what do you think?
>

I think your problem concerns not recognising a library defined operator.

when you wrote:

s1 + s2 = "Eh?";

the compiler sees:

operator=(s1+s2, "Eh?");

It evaluates s1+s2 as a temporary of type std::string and passes that
along with the second argument to the relevant operator=(). Where the
new features of C++11 cut in is in allowing (in this context) a
temporary to be bound to a non-const reference parameter

SG

unread,
Oct 3, 2012, 12:22:31 PM10/3/12
to
{ Please limit your text to fit within 80 columns, preferably around 70,
so that readers don't have to scroll horizontally to read each line.
This article has been reformatted manually by the moderator. -mod }

Am Mittwoch, 3. Oktober 2012 01:23:08 UTC+2 schrieb Simon Parmenter:
> Hi All
>
> I was just playing around with some test code I had done sometime ago and
> found some behaviour I could not explain; looking up the C++11 standard
> did me no good either.

You're invoking undefined behaviour. Simple as that. See my comments
below.

> int main()
> {
> std::string s1("Hello");
> std::string s2(" World");
> s1 + s2 = "Eh?";
> std::string & sr = (s1 + s2 = "Eh?");
> std::string const & sr1(s1 + s2);
> cout << (s1 + s2 = "Eh?") << endl;
> cout << sr << endl;
> cout << sr1 << endl;
> }

sr is a dangling reference. You initialized it to refer to a temporary
object which vanishes soon after that. The assignment operator just reterns
a reference to this object and the compiler loses the information that this
is actually a reference to a temporary (because the function signature
of operator= doesn't say it). So, the temporary life-time extension rule
that applies in the case for sr1 is not applicable to the case sr.


> int main()
> {
> std::string s1("Hello");
> std::string s2(" World");
> s1 + s2 = "Eh?";
> std::string & sr = (s1 + s2 = "Eh?");
> cout << sr << endl;

Same here. sr is a dangling reference. Accessing it invokes undefined
behaviour.

> std::string const & sr1(s1 + s2);

while for this line a special life-time extension rule kicks in. s1+s2
directly returns a temporary object (and not a reference like the
assignment operator) so the rule applies and the temporary's life-time
is extended to the life-time of the reference sr1.

You might want to check out the following article as well:
http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/


> int main()
> {
> std::string s1("Hello");
> std::string s2(" World");
> s1 + s2 = "Eh?";
> std::string & sr = (s1 + s2 = "Eh?");
> cout << sr << endl;

Once again, undefined behaviour.


> int main()
> {
> std::string s1{"Hello"};
> std::string s2{" World"};
> s1 + s2 = "Eh?";
> std::string const & sr{s1 + s2 = "Eh?"};
> cout << sr << endl;

Once again, undefined behaviour.


Cheers!
SG

James K. Lowden

unread,
Oct 4, 2012, 2:40:20 PM10/4/12
to
On Wed, 3 Oct 2012 09:19:10 -0700 (PDT)
Francis Glassborow <francis.g...@btinternet.com> wrote:

> when you wrote:
>
> s1 + s2 = "Eh?";
>
> the compiler sees:
>
> operator=(s1+s2, "Eh?");

Is that right? Doesn't operator=() still need to be a unary member
function?

Looks to me like s1 + s2 creates a temporary, to which "Eh?" is
assigned. Anything after that is an accident of implementation.

When I've defined operator+ on my own objects, it returned a const
object, just to prevent this very thing. I wonder why std::string
doesn't do that.

--jkl

Daniel Krügler

unread,
Oct 5, 2012, 1:15:52 AM10/5/12
to

Am 04.10.2012 20:40, schrieb James K. Lowden:
[..]
> When I've defined operator+ on my own objects, it returned a const
> object, just to prevent this very thing. I wonder why std::string
> doesn't do that.

I wouldn't recommend that: Returning const class types in C++11 is
actively preventing moving such rvalues within expressions.

HTH & Greetings from Bremen,

Daniel Krügler

Seungbeom Kim

unread,
Oct 6, 2012, 2:16:45 AM10/6/12
to
On 2012-10-04 11:40, James K. Lowden wrote:
>
> When I've defined operator+ on my own objects, it returned a const
> object, just to prevent this very thing. I wonder why std::string
> doesn't do that.

Because no one would write 's1 + s2 = "Eh?"' and expect the result of
the assignment to be found later in any variable? (It's different from
the situation in 'void incr(double&); int i; incr(i);' where there
would be people expecting the side effect to be observed in i later.)

It also prevents usage patterns like 'f((s1+s2).replace(...).substr(...))'
which can be useful sometimes.

--
Seungbeom Kim

Gene Bushuyev

unread,
Oct 6, 2012, 2:30:50 AM10/6/12
to
On Thursday, October 4, 2012 11:40:30 AM UTC-7, James K. Lowden wrote:
> On Wed, 3 Oct 2012 09:19:10 -0700 (PDT)
...
> When I've defined operator+ on my own objects, it returned a const
> object, just to prevent this very thing. I wonder why std::string
> doesn't do that.

That's not a good solution, as it was mentioned, it would hurt
performance by prohibiting moving temporaries. I think the right
solution is to supply correct ref-qualifier to the declaration of
operator=

basic_string& operator= (basic_string other) &
{
/*move guts from other*/;
return *this;
}

I wonder why the standard instead specifies these two overloads:
basic_string& operator=(const basic_string& str); basic_string&
operator=(basic_string&& str) noexcept;
0 new messages