Andrew Koenig

Dr. Dobb's Bloggers

Two Kinds of References

August 31, 2011

This post provides some of the background behind one of C++'s odder rules, namely the one that requires a nonconst reference to be attached to an lvalue.

This post provides some of the background behind one of C++'s odder rules, namely the one that requires a nonconst reference to be attached to an lvalue. This rule evolved over several years, and understanding it is a useful prelude to understanding how rvalue references work.

The idea behind a C++ reference is simple — at least in theory. A reference is nothing more than another name for an object. For example:

int a;
int& b = a;

We have defined b as a reference and initialized it from a, which means that b is just another name for a. Once we have defined a and b in this way, reading or writing the value of a has the same effect as reading or writing the value of b, and vice versa.

The most common use of references is as function parameters:

void clear(int& n) { n = 0; }

Here we have defined a function named clear that sets its argument to zero, so that if we call clear(a), with a defined as we did earlier, the effect is to set a to zero. Calling clear(b) has the same effect, because b is just another name for a.

What happens if we call clear with something other than a variable, such as clear(42) or clear(a+1)? It makes no sense to set 42 to zero, and although it would be possible to make a+1 zero by setting a to -1, it would be impossible for such a technique to work in general.

So the question arises: What should happen if a reference is initialized from something that is not a variable? Once upon a time, C++ answered this question by saying that the argument should be copied and the reference attached to the copy. In other words, the effect of clear(a+1) was similar to

int temp = a+1;
clear(temp);

This treatment allowed programmers to use references as a way for functions to avoid having to copy their arguments. For example, by writing

bool greater(string& s1, string& s2) { return s1 > s2; }

we could avoid having to copy s1 and s2 merely in order to compare them. In other words, from the start, references solved two distinct problems. Programmers sometimes wanted to avoid copying an argument in order to ensure that changes to the reference would be reflected in the argument; other times they wanted to avoid copying it for versions of runtime speed alone.

At some point, someone wrote a statement like

cin >> x + y;

by mistake instead of writing

cout << x + y;

and was surprised that it quietly compiled. Obviously, input and output are fine examples of the two different ways in which one might want to use references: One passes a variable as the second argument to >> because one intends to read into it, but << takes a reference merely for speed reasons.

After much discussion, the fledgling C++ community realized that these two uses were different, and, after much more discussion, decided to use const to distinguish between them. The idea is that when you bind a reference to a const object, it is obvious that you do not intend to use the reference to change the object — it's const, so you can't do it. If, on the other hand, you bind a reference to a nonconst object, then you're saying that you intend to use the reference to change the object. In that case, the compiler will require the object to be an lvalue:

The definition of d is permitted because the const is a promise that d will not be used to change the value of 42. Therefore, the compiler is permitted to copy 42 and attach d to the copy without having to worry that the programmer will be surprised after an attempt to change d.

The idea that references serve two different purposes persisted for many years. However, C++11 reflects a third use for references, which complements the first two:

A reference to nonconst must refer to the original object so that changes to the reference will be reflected as changes to the object.

A reference to const may refer to a copy of the object because the reference will not be used to change the object.

An rvalue reference may refer to the original object — even though the reference might be used to change the object — because the original object's value is no longer important.

In effect, an rvalue reference is how a programmer tells the compiler that an object is about to be discarded or reused, so that its current value can be recycled.

We'll see next week how such instructions to the compiler can be useful.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!