bolov
bolov

Reputation: 75698

Initializing a reference

Although I thought I understand the rvalue and lvalue semantics in C++, I seem to stomp over and over again into strange examples that prove to me that I don't know squat.

However there are two very simple and basic ones that I don't understand how they work. Before I compiled them I thought none would be ok, after I saw that (1) works, I thought that (2) would work too. However, (1) works, (2) doesn't:

(1) const std::string &s = "asd";

What happens here? My guess is that a temporary const string object is constructed from "asd", and then s is bound to that temporary object. But wouldn't then the temporary object be destroyed right after this line so we would be left with an invalid reference?

When I drop the const qualifier:

(2) std::string &s = "asd";

I get a compiler error (VS 2013): cannot convert from 'const char [4]' to 'std::string &'. Which seems to disprove my theory, because, according to it (my guess), a temporary string object would be constructed from "asd" and then s assigned to it, which wouldn't generate any compile error.

So to sum up:

Upvotes: 4

Views: 229

Answers (3)

Mike Seymour
Mike Seymour

Reputation: 254431

To what is s binded?

To a temporary std::string constructed from the string literal, as you surmised.

What is the lifespan of the object that s is binded to?

The temporary's lifetime is extended to match the reference's. Usually, temporaries are destroyed at the end of the full-expression that creates them; this is an exception to that rule.

What makes (1) compile and (2) not?

Another language rule: temporaries can't be bound to non-const references. This rule is a bit quirky, but useful: it prevents subtle bugs if you were to accidentally modify a temporary instead of a more persistent object.

Upvotes: 2

Zeta
Zeta

Reputation: 105886

TL;DR

  • To what is s binded?
  • What is the lifespan of the object that s is binded to?

To a newly created temporary std::string, that will life as long as s.

What makes (1) compile and (2) not?

You may not make a non-const reference to non-lvalues.

Relevant quotes

Quoting from 8.5.3 [dcl.init.ref]:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • [ .. snip, not relevant .. ]
  • Otherwise [if the right hand side is not an lvalue], the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
    • [ .. snip, not relevant .. ]
    • Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 shall be the same cv-qualification as, or greater cv-qualification than, cv2. If T1 is reference-related to T2 and the reference is an rvalue reference, the initializer expression shall not be an lvalue.

In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

In your case, you need to convert const char* to std::string, which introduces a temporary of type std::string. Since this is not an lvalue, the reference is shall be const. This concludes why (2) doesn't work.

For the lifetime, have a look at 12.2 [class.temporary]:

There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. [...]

The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference [...]

This means that the temporary will as long as your const reference. This concludes why (1) does work.

Note that there are many more details in the standard, which include some corner cases, so you might want to read those sections completely.

Upvotes: 2

Jeroen Baert
Jeroen Baert

Reputation: 1314

(2) Doesn't work because "asd" is a char const[]. The compiler will implicitly convert it (indeed) to a temporary string rvalue. You cannot assign a non-const reference to a value which will immediately go out of scope. So your analysis there is correct.

For (1) I had to do a little lookup myself, but I think I've found the answer here : Does a const reference prolong the life of a temporary?

It appears that the standard allows a copy to occur when binding a temporary to a const reference. Unexpexted, I agree :) So there will be an implicit conversion again to a temporary string rvalue again, and because the reference is now const, this rvalue will be copied so it persists.

Upvotes: 3

Related Questions