Constexpr

From Chilipedia
Jump to: navigation, search

constexpr is a keyword applied to variables and functions. It's basically the same as the [const] keyword but enforces an additional rule that "The value of this const will be available at compile time"

Difference between const & constexpr

Basics

In C++ there can be 2 types of constants:

  1. Whose value is known at compile time.

const int x = 50;
const int z = x + 1;

During compile time, the compiler knows the value of x and z would be 50 and 51 respectively. And the values won’t change throughout the lifetime of the program. So it goes “I’ll just substitute all the references to x and z with 50 and 51, instead of looking up what the values of x and z every time during runtime”.

This allows 50 and 51 to be used as literals (hardcoded values) with all their advantages and at the same time not clutter our code with random numbers that are hard to interpret the meaning of.

  1. Whose value is not known at compile time.

const int y = GenRandomNumber();

In this case the value of y would be impossible to determine at compile time as a we don’t know what random number would be generated at runtime.

NOTE: the variable is still a constant, once a random number is assigned to it the value cannot change.

When the compiler comes across scenario #1 it marks x an z as constant expressions i.e. their value can be evaluated at compile time and in scenario #2 marks y as being a constant but not a constant expression as it can only be evaluated during runtime.

This can be a bit confusing in certain contexts e.g.

int anArray[x]; // Compiles successfully 
int anotherArray[y]; // Fails compilation

Certain operations require compile-time knowledge of a variable, in this case, anotherArray declaration would throw an error even though y is a const just like x. This is because the value of y cannot be computed until the function GenRandomNum() is run.

Due to this confusion, it is hard to evaluate what constants will be available during compile time, and which won’t like in our example above. As your code base gets bigger this can spiral out of control, as multiple people are working on the same code base.

This is where the keyword constexpr comes in as it tells the compiler that the value of this variable should be deduced at compile time to allow for scenarios such as the one above. If y was declared as constexpr int y = GetRandomNumber() the compiler would throw an error that the value cannot be evaluated at compile time. It clarifies the intended use of the variable.

constexpr int y = GetRandomNumber(); // error
constexpr int y = 69; // Compiles fine

Advanced

Pointers

constexpr Pointers are implicitly const, i.e you cannot change where the pointer points to but you can change the data at the memory address.

static constexpr int x = 5
static constexpr int y = 10;
constexpr int* integer = &x; // Equivalent to 'int* const integer = &x' but evaluated at compile time

integer = &y // Error, cannot retarget the pointer
*integer = 15 // Valid, value of x is changed to 15

To ensure the pointer is evaluated at compile time, cannot be retargeted and is not able to change its target a const keyword can be added after constexpr

static constexpr int x = 5;
static constexpr int y = 10;
constexpr const int* integer = &x; // Equivalent to 'const int* const integer = &x' but guaranteed to be evaluated at compile time.

integer = &y;  // Error, Cannot Retarget the pointer
*integer = 15; // Error, the value at the pointed address cannot be modified.

Functions

Functions can also be evaluated at compile time by using the keyword constexpr which means "should be usable in a constant expression when given constant expressions as arguments."

constexpr double square(double x) {return x * x;}

If the arguments to a function is a constexpr the function returns a constexpr.

To declare a function as a constexpr, the function cannot contain:

  1. ASM definition {movl $7, %eax}
  2. A goto statement
  3. A statement with a label other than case and default {label: take that compiler}
  4. A try-block {try {std::cout << "My dick stinks" << std:endl; }}
  5. Variable definition of non-literal type {MyCustomClass anObject}
  6. Static variable
  7. Uninitialized variable

Why do you care? Constexpr function are a good way to write cleaner code as compared to using macros everywhere, improve performance (but rarely used for this purpose)

Constexpr Classes

Simple user-defined classes can also be used as constant expressions.

To make a class a constexpr class it needs to have a constexpr constructor with an empty body and all member functions should be initialized by constant expressions.

Declaring a constructor as constexpr turns your class into "User Defined Literals" i.e. you class can now be used as a literal


Planet Chili Tutorial Video

Other Tutorials

See Also