Skip to main content

C++ Primer Sec.2.4 const qualifier

const Qualifier

Because we can’t change the value of a const object after we create it, it must be initialized just as reference

const int i = get_size(); // ok: initialized at run time
const int j = 42; // ok: initialized at compile time
const int k; // error: k is uninitialized const

when we use an object to initialize another object, it doesn’t matter whether either or both of the objects are consts.

int i = 42;
const int ci = i; // ok: the value in i is copied into ci
int j = ci; // ok:the value in ci is copied into j
By Default, const Objects Are Local to a File

When we really need to share const between files, define a single instance of a const variable, we use the keyword extern on both its definition and declaration(s).

// file_1.cc defines and initializes a const that is accessible to other files
extern const int bufSize = fcn();
// file_1.h
extern const int bufSize; // same bufSize as defined in file_1.cc
References to const
const int ci = 1024;
const int &r1 = ci; // ok: both reference and underlying object are const
r1 = 42; // error: r1 is a reference to const
int &r2 = ci; // error: non const reference to a const object
exception

we noted that there are two exceptions to the rule that the type of a reference must match the type of the object to which it refers. The first exception is that we can initialize a reference to const from any expression that can be converted (§ 2.1.2, p. 35) to the type of the reference.

int i = 42;
const int &r1 = i; // we can bind a const int& to a plain int object
const int &r2 = 42; // ok: r2 is a reference to const
const int &r3 = r1 * 2; // ok: r3 is a reference to const
int &r4 = r1 * 2; // error: r4 is a plain, non const reference

A reference to const may refer to an object that is not const

int i = 42;
int &r1 = i; // r1 bound to i
const int &r2 = i; // r2 also bound to i; but cannot be used to change i
r1=0; // r1 isnot const;i isnow 0
r2 = 0; // error: r2 is a reference to const
Pointers and const

A const pointer/reference/value can = (initialized with) a const or non-const, but a non-const cannot = to a const

const double pi = 3.14; // pi is const; its value may not be changed
double *ptr = π // error: ptr is a plain pointer
const double *cptr = π // ok: cptr may point to a double that is const
*cptr = 42; // error: cannot assign to *cptr
Const pointer

pointer itself is const

Like any other const object, a const pointer must be initialized and once initialized, its value may not be changed

The fact that a pointer is itself const says nothing about whether we can use the pointer to change the underlying object. Whether we can change that object depends entirely on the type to which the pointer points.

int errNumb = 0;
int *const curErr = &errNumb; // curErr will always point to errNumb
const double pi = 3.14159;
const double *const pip = π // pip is a const pointer to a const object

Top-level const

Top-level const to indicate that a pointer itself is a const.

when a pointer to a const object, we refer to that const as a low-level const.

when copy an object, the low-level const should be the same. i.e., both should have the pointed object as const.

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

constexpr and constant expressions

declare constexpr for compiler to verify that it is a constant expression

Variables declared as constexpr are implicitly const and must be initialized by constant expressions

constexpr int mf = 20; // 20 is a constant expression
constexpr int limit = mf + 1; // mf + 1 is a constant expression
constexpr int sz = size(); // ok only if size is a constexpr function

Although we cannot use an ordinary function as an initializer for a constexpr variable, we’ll see in § 6.5.2 (p. 239) that the new standard lets us define certain functions as constexpr. Such functions must be simple enough that the compiler can evaluate them at compile time. We can use constexpr functions in the initializer of a constexpr variable.

Literal types

The arithmetic, reference, and pointer types are literal types.

when pointers and reference are defined as constexpr, they need to point to or refer to objects that remains at a fixed address

such as variables defined inside a function can not be pointed to, because they are not stored at a fixed address. See § 6.1.1 for details.

There are variables defined outside any function, those special object also have fixed addresses, so can be used as the target object.

Pointers and constexpr

when we define a pointer in a constexpr declaration, the constexpr specifier applies to the pointer, not the type to which the pointer points.

constexpr int *np = nullptr; // np is a constant pointer to int that is null int j = 0;
constexpr int i = 42; // type of i is const int
// i and j must be defined outside any function
constexpr const int *p = &i; // p is a constant pointer to the const int i
constexpr int *p1 = &j; // p1 is a constant pointer to the int j

Comments

Popular posts from this blog

C++ Primer Sec. 2.6 Defining our own data structures

Defining our own data structures Class definition must have ; at the end. Preprocessor The most common technique for making it safe to include a header multiple times relies on the preprocessor. #include Header guards #ifndef SALES_DATA_H #define SALES_DATA_H #include struct Sales_data { std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; #endif SALES_DATA_H can be any name, just it has to be unique throughout the program. preprocessor variables usually are written in all uppercase.

C++ Primer Sec. 2.5 Dealing with Types

Dealing with Types type alias typedef double wages; // wages is a synonym for double typedef wages base, *p; // base is a synonym for double, p for double* C++11: alias declaration using SI = Sales_item; // SI is a synonym for Sales_item As usual, a const that appears in the base type modifies the given type. The type of pstring is “pointer to char.” So, const pstring is a constant pointer to char—not a pointer to const char. It can be tempting, albeit incorrect, to interpret a declaration that uses a type alias by conceptually replacing the alias with its corresponding type. typedef char *pstring; const pstring cstr = 0; // cstr is a constant pointer to char const pstring *ps; // ps is a pointer to a constant pointer to char const char *cstr = 0; // wrong interpretation of const pstring cstr C++11: auto. auto tells the compiler to deduce the type from the initializer. The initializers for all the variables in the declaration using auto must have types that a...