Search

6.11a — References and const

By Alex on June 7th, 2017 | last modified by Alex on October 21st, 2017

Reference to const value

Just like it’s possible to declare a pointer to a const value, it’s also possible to declare a reference to a const value. This is done by declaring a reference using the const keyword.

1

2

constintvalue=5;

constint&ref =value;// ref is a reference to const value

References to const values are often called “const references” for short.

Initializing references to const values

Unlike references to non-const values, which can only be initialized with non-const l-values, references to const values can be initialized with non-const l-value, const l-values, and r-values.

1

2

3

4

5

6

7

intx=5;

constint&ref1 =x;// okay, x is a non-const l-value

constinty=7;

constint&ref2 =y;// okay, y is a const l-value

constint&ref3 =6;// okay, 6 is an r-value

Much like a pointer to a const value, a reference to a const value can reference a non-const variable. When accessed through a reference to a const value, the value is considered const even if the original variable is not:

1

2

3

4

5

intvalue=5;

constint&ref =value;// create const reference to variable value

value=6;// okay, value is non-const

ref=7;// illegal -- ref is const

A reference to a const is often called a const reference for short, though this does make for some inconsistent nomenclature with pointers.

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

Normally r-values have expression scope, meaning the values are destroyed at the end of the expression in which they are created.

1

std::cout<<2+3;// 2 + 3 evaluates to r-value 5, which is destroyed at the end of this statement

However, when a reference to a const value is initialized with an r-value, the lifetime of the r-value is extended to match the lifetime of the reference.

1

2

3

4

5

6

intsomefcn()

{

constint&ref =2+3;// normally the result of 2+3 has expression scope and is destroyed at the end of this statement

// but because the result is now bound to a reference to a const value...

std::cout<<ref;// we can use it here

}// and the lifetime of the r-value is extended to here, when the const reference dies

Const references as function parameters

References used as function parameters can also be const. This allows us to access the argument without making a copy of it, while guaranteeing that the function will not change the value being referenced.

1

2

3

4

5

// ref is a const reference to the argument passed in, not a copy

voidchangeN(constint&ref)

{

ref=6;// not allowed, ref is const

}

References to const values are particularly useful as function parameters because of their versatility. A const reference parameter allows you to pass in a non-const l-value argument, a const l-value argument, a literal, or the result of an expression:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#include <iostream>

voidprintIt(constint&x)

{

std::cout<<x;

}

intmain()

{

inta=1;

printIt(a);// non-const l-value

constintb=2;

printIt(b);// const l-value

printIt(3);// literal r-value

printIt(2+b);// expression r-value

return0;

}

The above prints

1234

To avoid making unnecessary, potentially expensive copies, variables that are not pointers or fundamental data types (int, double, etc…) should be generally passed by (const) reference. Fundamental data types should be passed by value, unless the function needs to change them.

Hey, what if I passed a class by const reference and then tried to access to one of its methods that DO NOT MODIFY its state ?
I tried this because I needed using getters of a certain class I passed by const.ref. and I got an error.
Does this mean passing a class in this way prevents us from calling its methods ?

You could do so and certainly should in small programs. However, when you're writing bigger projects you'll find yourself with several variables which are used in many places. Passing those around as arguments is tedious, so you'll use global variables or singletons.

I read further into the tutorials so I understand pointers and references better, so I understand what happens in program 2, but check this out (I'm also on Visual Studio). Turns out in the first program I actually get an error:

So it was printing the second program instead (as I tried that one first), which still gives me 5 5 5 on the second line.

I tried to think the flow of the second program through:

The program starts executing at main: i = 2, j = 5. i is then sent to a (an A struct) so "a.i" _literally is_ i, since &i is a reference variable. Then i (still 2), j (still 5) and a.i (refers to i, so 2) are printed.

Then I make a.i (literally i) have j's value, so i must have j's value, so cout prints 5 5 5.

So I then tried figuring out what caused the "Illegal indirection" error in the first program.

The program starts executing at main: i = 2 and j = 5, just as before. a (an A struct) contains a pointer, to which &i (i's memory adress) is sent.., meaning a.i points to i.
i (still 2), j (still 5), and *a.i (a dereferenced pointer, pointing to i, so 2).

Then I make a.i point to &j (right?) so i SHOULD stay the same, j as well, but if I were to derefence *a.i, I'll get 5. So I assume this went fine on your machine and the computer correctly printed 2 5 2, 2 5 5. But mine for some reason found an error and ended up reprinting 2 5 2, 5 5 5 from the previous (here the second) program. What should I do about this?

Hi Alex, output is 555 should be second line. because A a({ i }); it means internally i value alias to &a.i, correct?, so second time assigning value j value to it will update in i also. so output is 555 for second line.

Hi, Alex.
At "References to r-values extent the life time of the referenced value"
What is the difference between
[const int &ref=2+3;]
and
[const int value=2+3;].
It seems that they're all extend the life time of "2+3", so is it too obvious
to say the statement above?

There's no need to double-ampersand here. A double-ampersanded reference variable is still treated as an l-value (after all, it has an address). So essentially, in such a case, the second ampersand is ignored.