Constexpr
constexpr
isn't really a template, but serves as a method of doing compile time computations.
constexpr
denotes that an expression may be evaluated as a compile-time constant expression, provided its dependencies
(function arguments, etc.) are also constant expressions.
A constexpr
variable must be: a literal type, immediately initialized, and the expression initializing the variable must also be a constant expression.
constexpr
variables must also have a constant destruction, meaning it is not an array or non-literal class. In C++20, a constexpr
variable can also be a class
provided it has a constexpr
destructor.
A constexpr
function must not be virtual
and its arguments and return type must be literal types. The function body of a constexpr
function
must not contain definitions of static or non-literal variables, try blocks, inline assembly, or uninitialized variable definition. The latter three are
no longer required in C++20.
We've discussed what a literal type is before: scalars or primitive types, references, and arrays of literals, just to name a few examples.
However, literal types can also be literal classes, which are classes with a trivial destructor.
Literal classes must also have at least one constexpr
constructor, be an aggregate type (struct with no user-defined constructor or union),
or be a closure type (lambda). Literal classes must have data members that are also literals.
The constexpr
constructor of a literal class must satisfy the requirements of a constexpr
function and initialize every data member. C++20 allows
constexpr
destructors, however in C++17 the destructor must be compiler-generated.
Literal classes may also have constexpr
member functions, which are not implicitly const
members (they were in C++11).
So, making a constexpr
member unable to change the state of the class requires the const
qualifier just like a normal member function.
class Rational {
int num, den;
public:
constexpr Rational(int numerator, int denominator = 1) :
num(numerator), den(denominator) {}
constexpr explcit operator double() const {
return num / static_cast<double>(den);
}
constexpr Rational operator-() const {
return {-num, den};
}
constexpr Rational& operator++() {
num += den;
return *this;
}
friend constexpr auto operator*(const Rational& a, const Rational& b) {
return Rational(a.num * b.num, a.den * b.den);
}
};
If an exception is thrown during a constant expression during compile-time evaluation, that exception stops compilation. The compiler error you'll probably see might be something along the lines of "... failed to result in a constant expression".