Search

8.4 — L-value references to const

In the previous lesson (8.3 -- L-value references), we discussed how an l-value reference can only bind to a modifiable l-value. This means the following is illegal:

But what if we want to have a reference to a non-modifiable l-value?

L-value reference to a const value

We can tell an l-value reference to treat the object it is referencing as const by using the const keyword when declaring the reference. This makes the l-value reference an l-value reference to a const value (sometimes called a reference to const or a const reference).

L-value references to const can bind to non-modifiable l-values:

Because l-value references to const treat their values as const, they can be used to access but not modify the value being referenced:

Initializing an l-value reference to const with a modifiable l-value

L-value references to const can also bind to modifiable l-values:

When a lvalue reference to a const value binds to a modifiable l-value, the value is treated as const when accessed through the reference, even though the underlying value is non-const. For this reason (as shown in the program above), we can modify the value of x through variable x (which is non-const), but not through ref (which treats the value as const).

Rule

Favor l-value references to const over l-value references to non-const unless you need to modify the object being referenced.

Initializing an l-value reference to const with an r-value

Perhaps surprisingly, l-values references to const can also bind to r-values.

When this happens, a temporary object is created and initialized with the r-value, and the reference is bound to that temporary object.

A temporary object (also sometimes called an unnamed object) is an object that has no name. Temporary objects have no scope at all (this makes sense, since scope is a property of an identifier, and temporary objects have no identifier). This means a temporary object can usually only be used at the point where it is created, since there is no way to refer to it beyond that point.

References to r-values extend the lifetime of the referenced value

Normally, temporary objects are destroyed at the end of the expression in which they are created.

Consider what would happen in the above example if the temporary object created to hold r-value 5 was destroyed at the end of the expression that initializes ref. Reference ref would be left dangling (referencing an object that had been destroyed), and we’d get undefined behavior when we tried to access the value of ref.

To avoid dangling references in such cases, C++ has a special rule: When a reference to a const value is initialized with an r-value, the lifetime of the temporary object created is extended to match the lifetime of the reference. This is the exception to the rule that references and referents have independent lifetimes.

In the above example, when ref is initialized with r-value 5, a temporary object is created and ref is bound to that temporary object. The lifetime of the temporary object matches the lifetime of ref. Thus, we can safely print the value of ref in the next statement. Then both ref and the temporary object die at the end of the block.

Key insight

L-value references can only bind to modifiable l-values.

L-value references to const can bind to both modifiable and non-modifiable l-values, as well as r-values. This makes them a much more flexible type of reference.

So why does C++ allow an l-value references to const to bind to an r-value anyway? We’ll answer that question in the next lesson.


8.5 -- Pass by reference
Index
8.3 -- L-value references

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">