© Mikael Olsson 2018
Mikael OlssonC++17 Quick Syntax Referencehttps://doi.org/10.1007/978-1-4842-3600-0_23

23. Constants

Mikael Olsson1 
(1)
Hammarland, Finland
 

A constant is a variable that has a value that cannot be changed once the constant has been assigned. This allows the compiler to enforce that the variable’s value is not changed anywhere in the code by mistake.

Constant Variables

A variable can be made into a constant by adding the const keyword either before or after the data type. This modifier means that the variable becomes read-only, and it must therefore be assigned a value at the same time as it is declared. Attempting to change the value anywhere else results in a compile-time error.

const int var = 5;
int const var2 = 10; // alternative order

Constant Pointers

When it comes to pointers, const can be used in two ways. First, the pointer can be made constant, which means that it cannot be changed to point to another location.

int myPointee;
int* const p = &myPointee; // pointer constant

Second, the pointee can be declared constant. This means that the variable pointed to cannot be modified through this pointer.

const int* q = &var; // pointee constant

It is possible to declare both the pointer and the pointee as constant to make them both read-only.

const int* const r = &var; // pointer & pointee constant

Note that constant variables may not be pointed to by a non-constant pointer . This prevents programmers from accidentally rewriting a constant variable using a pointer.

int* s = &var; // error: const to non-const assignment

Constant References

References can be declared constant in the same way as pointers. However, since reseating a reference is never allowed, declaring the reference as const would be redundant. It only makes sense to protect the referee from change.

const int& y = var; // referee constant

Constant Objects

Just as with variables, pointers , and references, objects can also be declared constant. Take the following class as an example.

class MyClass
{
 public:
  int x;
  void setX(int a) { x = a; }
};

A constant object of this class cannot be reassigned to another instance. The const-ness of an object also affects its fields and prevents them from being changed.

const MyClass a, b;
a = b;    // error: object is const
a.x = 10; // error: object field is const

Constant Methods

Because of this last restriction, a constant object may not call a non-constant method since such methods are allowed to change the object’s fields.

a.setX(2); // error: cannot call non-const method

They may only call constant methods, which are methods that are marked with the const modifier before the method body.

int getX() const { return x; } // constant method

This const modifier means that the method is not allowed to modify the state of the object and can therefore safely be called by a constant object of the class. More specifically, the const modifier applies to the this pointer that is implicitly passed to the method. This effectively restricts the method from modifying the object’s fields or calling any non-constant methods in the class.

Constant Return Type and Parameters

In addition to making a method constant, the return type and method parameters may also be made read-only. For example, if a field is returned by reference instead of by value from a constant method, it is important that it is returned as a constant in order to maintain the const-ness of the object. Not all C++ compilers will be able to catch this subtle mistake.

const int& getX() const { return x; }

Constant Fields

Both static and instance fields in a class can be declared constant. A constant instance field must be assigned its value using the constructor initialization list. This is the same as the preferred way of initializing regular (non-constant, non-static) fields.

class MyClass
{
 public:
  int i;
  const int c;
  MyClass() : c(5), i(5) {}
}

A constant static field has to be defined outside of the class declaration, in the same way as non-constant static fields. The exception to this is when the constant static field is of an integer data type. Such a field may also be initialized within the class at the same time as the field is declared.

class MyClass
{
 public:
  static int si;
  const static double csd;
  const static int csi = 5;
};
int MyClass::si = 1.23;
const double MyClass::csd = 1.23;

Constant Expressions

The keyword constexpr was introduced in C++11 to indicate a constant expression. Like const it can be applied to variables to make them constant, causing a compilation error if any code attempts to modify the value.

constexpr int myConst = 5;
myConst = 3; // error: variable is const

Unlike const variables, which may be assigned at runtime, a constant expression variable will always be computed at compile time. Such a variable can therefore be used whenever a compile-time constant is needed, such as in array or enum declarations. Prior to C++11, this was only allowed for constant integer and enumeration types.

int myArray[myConst + 1]; // allowed

Functions and class constructors may also be defined as constant expressions , which is not allowed with const. Using constexpr on a function limits what the function is allowed to do. In short, the function must consist of a single return statement, and it can only reference other constexpr functions and global constexpr variables. C++14 relaxes these constraints, allowing constexpr functions to contain other executable statements.

constexpr int getDefaultSize(int multiplier)
{
  return 3 * multiplier;
}

The return value for a constexpr function is guaranteed to be evaluated at compile time only when its arguments are constant expressions and the return value is used where a compile-time constant is necessary.

// Compile-time evaluation
int myArray[getDefaultSize(10)];

If the function is called without constant arguments , it returns a value at runtime just like a regular function.

// Runtime evaluation
int mul = 10;
int size = getDefaultSize(mul);

As of C++17, a lambda expression is implicitly constexpr if it satisfies the conditions of a constexpr function. Such a lambda may therefore also be used in a compile-time context.

auto answer = [](int i) { return 10+i; };
constexpr int reply = answer(32); // "42"

Constructors can be declared with constexpr, to construct a constant expression object. Such a constructor must be trivial.

class Circle
{
 public:
  int r;
  constexpr Circle(int x) : r(x) {}
};

When called with a constant expression argument, the result will be a compile-time generated object with read-only fields. With any other arguments, it will behave as an ordinary constructor.

// Compile-time object
constexpr Circle c1(5);
// Runtime object
int x = 5;
Circle c2(x);

One additional use for constexpr was added in C++17: the ability to evaluate conditional statements at compile time. This feature allows branches of an if statement to be discarded at compile time based on a constant condition, potentially reducing compilation time as well as the size of the compiled file.

constexpr int debug = 0;
if constexpr(debug) {
  // Discarded if condition is false
}

Constant Guideline

In general, it is a good idea to always declare variables as constants if they do not need to be modified. This ensures that the variables are not changed anywhere in the program by mistake, which in turn will help prevent bugs. There is also a performance gain by allowing the compiler the opportunity to hard-code constant expressions into the compiled program. This allows the expression to be evaluated only once—during compilation—rather than every time the program runs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.246.218