const
As we’ve seen, a pointer is an object that can point to a different object. As a result, we can talk independently about whether a pointer is const
and whether the objects to which it can point are const
. We use the term top-level const
to indicate that the pointer itself is a const
. When a pointer can point to a const
object, we refer to that const
as a low-level const
.
Exercise 2.27: Which of the following initializations are legal? Explain why.
(a) int i = -1, &r = 0;
(b) int *const p2 = &i2;
(c) const int i = -1, &r = 0;
(d) const int *const p3 = &i2;
(e) const int *p1 = &i2;
(f) const int &const r2;
(g) const int i2 = i, &r = i;
Exercise 2.28: Explain the following definitions. Identify any that are illegal.
(a) int i, *const cp;
(b) int *p1, *const p2;
(c) const int ic, &r = ic;
(d) const int *const p3;
(e) const int *p;
Exercise 2.29: Uing the variables in the previous exercise, which of the following assignments are legal? Explain why.
(a) i = ic;
(b) p1 = p3;
(c) p1 = ⁣
(d) p3 = ⁣
(e) p2 = p1;
(f) ic = *p3;
More generally, top-level const
indicates that an object itself is const
. Top-level const
can appear in any object type, i.e., one of the built-in arithmetic types, a class type, or a pointer type. Low-level const
appears in the base type of compound types such as pointers or references. Note that pointer types, unlike most other types, can have both top-level and low-level const
independently:
int i = 0;
int *const p1 = &i; // we can't change the value of p1; const is top-level
const int ci = 42; // we cannot change ci; const is top-level
const int *p2 = &ci; // we can change p2; const is low-level
const int *const p3 = p2; // right-most const is top-level, left-most is not
const int &r = ci; // const in reference types is always low-level
The distinction between top-level and low-level matters when we copy an object. When we copy an object, top-level const
s are ignored:
i = ci; // ok: copying the value of ci; top-level const in ci is ignored
p2 = p3; // ok: pointed-to type matches; top-level const in p3 is ignored
Copying an object doesn’t change the copied object. As a result, it is immaterial whether the object copied from or copied into is const
.
On the other hand, low-level const
is never ignored. When we copy an object, both objects must have the same low-level const
qualification or there must be a conversion between the types of the two objects. In general, we can convert a nonconst
to const
but not the other way round:
int *p = p3; // error: p3 has a low-level const but p doesn't
p2 = p3; // ok: p2 has the same low-level const qualification as p3
p2 = &i; // ok: we can convert int* to const int*
int &r = ci; // error: can't bind an ordinary int& to a const int object
const int &r2 = i; // ok: can bind const int& to plain int
p3
has both a top-level and low-level const
. When we copy p3
, we can ignore its top-level const
but not the fact that it points to a const
type. Hence, we cannot use p3
to initialize p
, which points to a plain (nonconst
) int
. On the other hand, we can assign p3
to p2
. Both pointers have the same (low-level const
) type. The fact that p3
is a const
pointer (i.e., that it has a top-level const
) doesn’t matter.
Exercise 2.30: For each of the following declarations indicate whether the object being declared has top-level or low-level const
.
const int v2 = 0; int v1 = v2;
int *p1 = &v1, &r1 = v1;
const int *p2 = &v2, *const p3 = &i, &r2 = v2;
Exercise 2.31: Given the declarations in the previous exercise determine whether the following assignments are legal. Explain how the top-level or low-level const
applies in each case.
r1 = v2;
p1 = p2; p2 = p1;
p1 = p3; p2 = p3;