In C++, the constexpr
specifier is used to declare a function or variable to evaluate the value of at compile time, which speeds up code during runtime. This useful property had some restrictions in C++11, these are relaxed in C++14 and this feature is known as Relaxed Constexpr Restrictions. In this post, we explain what are the relaxed constexpr
restrictions in modern C++.
Table of Contents
What is the constexpr specifier in modern C++?
The C++11 standard comes with the introduction of generalized constexpr
functions. The constrexpr
is used as a return type specifier of function and improves performance by computations done at compile time rather than run time. Return values of constexpr could be consumed by operations that require constant expressions, such as an integer template argument.
Here is the syntax:
1 2 3 |
constexpr <function_definition> |
Here is an example of how to use a constexpr
in C++:
1 2 3 4 5 6 |
constexpr double sq(const double x) { return ( x*x ); } |
and can be used as shown below.
1 2 3 |
constexpr double y = sq(13.3); |
Note that here we used a constant variable 13.3
, that is given in coding, thus the result y
will be calculated during compilation as 176.89
. Then, we can say, similarly this line will be compiled as we show below:
1 2 3 |
const double y = 176.89 |
Is there a simple constexpr specifier example in modern C++?
Here we can sum all above in an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> // C++11, a simple constexpr example constexpr double sq(const double x) { return ( x*x ); } int main() { constexpr double y = sq(13.3); std::cout << y << std::endl; system("pause"); return 0; } |
What are the relaxed Constexpr restrictions in Modern C++?
C++11 constexpr functions could only contain a single expression that is returned. In addition to these, the relaxation of constexpr
restrictions is added in C++14, thus C++ was more expressive and useful in compile-time computations.
The C++14 standard relaxes restrictions in C++11. According to this standard, constexpr-declared functions may now contain the following,
- Any declarations except static or thread_local variables or except variable declarations without initializers.
- The conditional branching statements if and switch
- Any looping statement, including range-based for.
- Expressions that change the value of an object if the lifetime of that object began within the constant expression function. This includes calls to any non-const constexpr-declared non-static member functions.
With the arrival of the C++14 standard, the constexpr
functions can use multiple if in return statements too.
And there is a restriction in C++14, goto
statements are not allowed in relaxed constexpr-declared functions of C++14.
In C++11, all non-static member functions that were declared constexpr were also implicitly declared const, concerning this. This has been removed in C++14, non-static member functions can be non-const and a non-const constexpr member function can only modify a class member if that object’s lifetime began within the constant expression evaluation.
In C++ 11, a constexpr function should contain only one return statement. C++ 14 allows more than one statement. In C++11, prefix increment (++i
) was not allowed in the constexpr function but this restriction has been removed in C++14.
Are there any examples of relaxed constexpr restrictions in C++14?
Here are some examples of relaxed constexpr restrictions in C++14.
In C++14 and above, the conditional branching statements if and switch can be used in functions as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <iostream> constexpr int sw(char c) { if(c>0) { switch(c) { case 'a': return 0; break; case 'g': return 50; break; case 'u': return 500; break; } } } int main() { constexpr int l = sw('a'); std::cout << l << std::endl; system("pause"); return 0; } |
In C++14, constexpr
functions can use local variables and loops as in example like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <iostream> constexpr int factorial(int n) // C++14 { int ret = 1; for(int i=n; i>=1; i--) ret *= i; return ret; } int main() { constexpr int z = factorial(10); std::cout << z << std::endl; system("pause"); return 0; } |
In C++14, constexpr
functions can use multiple if in return statements as we show here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> // C++14: constexpr functions can use multiple if in return statement constexpr char checksize( int x) { return x > 8 ? '+' : x < 8 ? '-' : '0'; } int main() { constexpr char c = checksize(8); std::cout << c << std::endl; system("pause"); return 0; } |
For more information about this feature, please see https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2448r0.html and https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2448r1.html
C++ Builder is the easiest and fastest C and C++ IDE for building simple or professional applications on the Windows, MacOS, iOS & Android operating systems. It is also easy for beginners to learn with its wide range of samples, tutorials, help files, and LSP support for code. RAD Studio’s C++ Builder version comes with the award-winning VCL framework for high-performance native Windows apps and the powerful FireMonkey (FMX) framework for cross-platform UIs.
There is a free C++ Builder Community Edition for students, beginners, and startups; it can be downloaded from here. For professional developers, there are Professional, Architect, or Enterprise versions of C++ Builder and there is a trial version you can download from here