Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing a reference

Tags:

c++

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:

  • To what is s bound?
  • What is the lifespan of the object that s is bound to?
  • What makes (1) compile and (2) not (is it some conversion operators defined in std::string or is it a C++ semantic)?
like image 634
bolov Avatar asked Mar 21 '23 01:03

bolov


2 Answers

(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.

like image 176
Jeroen Baert Avatar answered Apr 05 '23 21:04

Jeroen Baert


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.

like image 29
Zeta Avatar answered Apr 05 '23 23:04

Zeta