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

issue with temporaries and const references

2 views
Skip to first unread message

noir

unread,
Oct 17, 2010, 4:44:32 AM10/17/10
to
Hello,
why does 2) below fail?


#include <cassert>
#include <string>

int main()
{
// 1) WORKS
std::string s = std::string("hello").append("world");
assert(s == "helloworld");

// 2) FAILS
const std::string& t = std::string("hello").append("world");
assert(t == "helloworld");

return 0;
}


My understanding about case 2) is:

- a temp string is created (OK)
- a non-const member (append) is called on the temp string (OK)
- a non-const reference to the temp string is returned by append (OK)
- the non-const reference is bound to a const-reference (OK)
- the const-reference extends the lifetime of the temp string until the
end of the block.

However, I see "t" contains trash when it is tested in the assert, so I
guess the destructor for the string has been called somewhere.

Any clues?

(Btw, I am testing this on VS 2008 SP2)

Alf P. Steinbach /Usenet

unread,
Oct 17, 2010, 5:33:13 AM10/17/10
to
* noir, on 17.10.2010 10:44:

Because 'append' returns a reference. The named reference is bound directly to
that reference, which is to a temporary whose lifetime ends after evaluation of
the initialization expression. If you tack on a '+ ""' you should avoid this,
but for your concrete example there's no point: the first declaration works.

Cheers & hth.,

- Alf

--
blog at <url: http://alfps.wordpress.com>

noir

unread,
Oct 17, 2010, 6:01:19 AM10/17/10
to
Il 17/10/2010 11.33, Alf P. Steinbach /Usenet ha scritto:
> * noir, on 17.10.2010 10:44:

> Because 'append' returns a reference. The named reference is bound


> directly to that reference, which is to a temporary whose lifetime ends
> after evaluation of the initialization expression. If you tack on a '+
> ""' you should avoid this, but for your concrete example there's no
> point: the first declaration works.


So I just got a reference to a dying reference? Wow, yet another dark
corner of C++.

Anyway, it is somewhat ironic that you mention the '+' operator when all
my troubles began when I tried to kill it.

Let me explain: I was trying to avoid the quadratic complexity of code
like this:

std::string s = "muchtext" + "muchtext2" + "muchtext3" + "muchtext4";

changing it to:

const std::string& s = std::string("muchtext")
.append("muchtext2")
.append("muchtext3")
.append("muchtext4");

but then the program crashed.

Thank you

Johannes Schaub (litb)

unread,
Oct 17, 2010, 6:09:10 AM10/17/10
to
noir wrote:

You can interpret the Standard as having two things that are called
"temporary". The Standard is not always clear what thing it refers to in all
places, apparently assuming the reader can deduce that from context.

The first thing is a temporary *object*. The reference that "append" returns
surely refers to that temporary object.

The second thing is a temporary *expression*. append does not return a
temporary expression. Temporary expressions are the ones that directly
represent the creation of a temporary object. I.e 'string("hello")'.

I doubt that this two-fold identity of "temporary" is actually intended by
the committee, but such is definitely present. See http://www.open-
std.org/jtc1/sc22/wg21/docs/cwg_active.html#462 for a discussion where the
property of an *expression* that was temporary would have been lost, and
explicit wording needs added to restore that property. On the other side,
other wording like at [class.copy]/p15 uses the *object* property of
"temporary" when it says "when a temporary class object that has not been
bound to a reference (12.2) would be copied ...". In the end, I think this
is disgusting.

The issue you mention was posted by me to usenet months ago, but it wasn't
forwarded as an issue report to the committee it seems. (And since the
reflector mailing list is kept behind closed doors, I have no clue whether
the committee ever discussed it).

Bo Persson

unread,
Oct 17, 2010, 6:16:50 AM10/17/10
to
noir wrote:
> Il 17/10/2010 11.33, Alf P. Steinbach /Usenet ha scritto:
>> * noir, on 17.10.2010 10:44:
>
>> Because 'append' returns a reference. The named reference is bound
>> directly to that reference, which is to a temporary whose lifetime
>> ends after evaluation of the initialization expression. If you
>> tack on a '+ ""' you should avoid this, but for your concrete
>> example there's no point: the first declaration works.
>
>
> So I just got a reference to a dying reference? Wow, yet another
> dark corner of C++.

No, it is just the usual "function returns a reference to a
temporary"-problem. We all know that is bad!

>
> Anyway, it is somewhat ironic that you mention the '+' operator
> when all my troubles began when I tried to kill it.
>
> Let me explain: I was trying to avoid the quadratic complexity of
> code like this:
>

> std::string s = std::string("muchtext") + "muchtext2" + "muchtext3"
> +
> "muchtext4";

The C++0x standard will help you out here, by eliminating most of the
temporaries using move assignment.

>
> changing it to:
>
> const std::string& s = std::string("muchtext")
> .append("muchtext2")
> .append("muchtext3")
> .append("muchtext4");
>
> but then the program crashed.

In the meantime you could try:

std::string s = "muchtext";
s += muchtext2;
s += muchtext3;
s += muchtext4;

or, if they are actually string literals, use the preprocessor feature
of concatenating adjacent literals:

std::string s =
"muchtext"

"muchtext2"
"muchtext3"
"muchtext4";


Bo Persson

noir

unread,
Oct 17, 2010, 6:22:45 AM10/17/10
to
Il 17/10/2010 12.16, Bo Persson ha scritto:

> The C++0x standard will help you out here, by eliminating most of the
> temporaries using move assignment.

Yep, I know, and I am looking forward to it :-)

noir

unread,
Oct 17, 2010, 6:43:35 AM10/17/10
to

Johannes,

thank you for your reply. The two different definitions of temporary
"things" are indeed very confusing.

I always bound to "temporary expressions" and I was very surprised the
code did't work.

Also, I would expect a compiler to issue some kind of warning about code
like that.

Luc Danton

unread,
Oct 17, 2010, 9:36:36 AM10/17/10
to
> The first thing is a temporary*object*. The reference that "append" returns

> surely refers to that temporary object.
>
> The second thing is a temporary*expression*. append does not return a

> temporary expression. Temporary expressions are the ones that directly
> represent the creation of a temporary object. I.e 'string("hello")'.
>
> I doubt that this two-fold identity of "temporary" is actually intended by
> the committee, but such is definitely present. Seehttp://www.open-> property of an*expression* that was temporary would have been lost, and

> explicit wording needs added to restore that property. On the other side,
> other wording like at [class.copy]/p15 uses the*object* property of

> "temporary" when it says "when a temporary class object that has not been
> bound to a reference (12.2) would be copied ...". In the end, I think this
> is disgusting.
>
> The issue you mention was posted by me to usenet months ago, but it wasn't
> forwarded as an issue report to the committee it seems. (And since the
> reflector mailing list is kept behind closed doors, I have no clue whether
> the committee ever discussed it).
>

Could you expand on why this could be problematic/harmful? I was a bit
spooked by your post but after reading the active issue and thinking
about it I think I can make head or tails of it. When trying to bind a
reference to a temporary, the only thing we care about is that the
expression results in a temporary. As a property of an expression, this
allows the user _and_ the implementation to only consider that
expression and no further, correct? Whereas if we had to consider
'temporary' as a property of an object, both user and implementation
would have to inspect the details.

Given:
T const& t = expression(a0, ..., an);

As the property of an expression:
- if expression is a function call, we only need to check that the
signature is of the form T(Arg0, ..., ArgN) to know whether we have
temporary
- if it is a constructor call, we have a temporary
- operator comma is covered by your link (it inherits the temporary-ness
of the right-hand expression)
- what about the ternary operator? I admit I'm not so sure myself there
- other cases...?

As the property an object:
I assume the base case would be constructor calls, and we follow from there.
But what if the expression uses IO behind the covers? Can we
deterministically check whether the end result would be a temporary?

I suspect the bit of wording about temporary objects you mentioned
regarding [class.copy] is mumbo-jumbo legalese to allow implementations
to elide copies/moves with any sort of magic they want. Does the notion
appear elsewhere? Perhaps it could be reworded to avoid introducing the
notion altogether while keeping the intent, I don't know.

What I find also interesting is that the idiomatic way to write rvalue
qualified, chaining methods would be:

T // _not_ T&&
T::method(Arg0, ... ArgN) &&
{
return std::move(*this);
}

right? Which assumes a useful move constructor but that makes sense:
overloading on rvalue *this should be a special case, not the rule.
Furthermore you'd need a good case to even *need* this kind of
overloading: for std::string (which has a useful move constructor), I'd
sooner write:
std::string s = std::move(other_string.assign("world"));
than ask for an rvalue overload of assign, or write my own when dealing
with custom classes.

Goran

unread,
Oct 18, 2010, 12:29:56 AM10/18/10
to
On Oct 17, 12:01 pm, noir <nos...@nospam.no> wrote:
> Il 17/10/2010 11.33, Alf P. Steinbach /Usenet ha scritto:
>
> > * noir, on 17.10.2010 10:44:
> > Because 'append' returns a reference. The named reference is bound
> > directly to that reference, which is to a temporary whose lifetime ends
> > after evaluation of the initialization expression. If you tack on a '+
> > ""' you should avoid this, but for your concrete example there's no
> > point: the first declaration works.
>
> So I just got a reference to a dying reference? Wow, yet another dark
> corner of C++.
>
> Anyway, it is somewhat ironic that you mention the '+' operator when all
> my troubles began when I tried to kill it.
>
> Let me explain: I was trying to avoid the quadratic complexity of code
> like this:
>
> std::string s = "muchtext" + "muchtext2" + "muchtext3" + "muchtext4";

Well, if that was your problem, then you should have calculated the
total length first (e.g. accumulate + reserve), and then used append.
That would have been better anyhow. It may look like a premature
optimization, but all the pieces to perform it are already at your
disposal, it's easy enough, and whatever the target implementation
might be doing, it's highly unlikely that it wouldn't pay off.

Goran.

0 new messages