I'm studying the C++ programming language and in a chapter my book introduces me the concept of constant :
A constexpr symbolic constant must be given a value that is known at compile time
What is a value known at compile time ? Why are we in need of them ?
A constant expression means an expression that can be evaluated at compile-time (i.e. before the program runs, during compilation) by the compiler.
A constant expression can be used to initialize a variable marked with constexpr (referring to the C++11 concept). Such a variable gives the compiler the hint that it could be compile-time evaluated (and that might spare precious runtime cycles), e.g.
#include <iostream>
constexpr int factorial(int n) // Everything here is known at compile time
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
int main(void)
{
constexpr int f = factorial(4); // 4 is also known at compile time
std::cout << f << std::endl;
return 0;
}
Example
If you don't provide a constant expression, there's no way the compiler can actually do all this job at compile-time:
#include <iostream>
constexpr int factorial(int n) // Everything here is known at compile time
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
int main(void)
{
int i;
std::cin >> i;
const int f = factorial(i); // I really can't guess this at compile time..
// thus it can't be marked with constexpr
std::cout << f << std::endl;
return 0;
}
Example
The gain in doing compile-time extra-work instead of run-time work is performance gain since your compiled program might be able to use precomputed values instead of having to compute them from scratch each time. The more expensive the constant expression is, the bigger is the gain your program gets.
What is a value known at compile time ?
I think it makes more sense to talk about constant expressions. A constant expression has a value that is known at compile time. Roughly speaking, it may be simply a literal, the name of another variable (whose value again is known at compile time) or a complex expression involving sub-expressions with values known at compile time.
The quote states that the initializer of a variable declared with constexpr needs to be a constant expression. In particular there are requirements an expression must satisfy to be a constant expression; Those are listed here.
Examples are
constexpr int i = 54;
constexpr float f = 684; // Compile-time conversion from int to float
constexpr int func( int i )
{
return i*47 % 23;
}
constexpr auto value = func(i * f); // Okay; constexpr function called
// with arguments that, when substituted inside,
// yield constant expressions
Sometimes a value is actually known at compile time but the expression isn't a constant one according to standard. That includes
int i = 43;
constexpr int j = reinterpret_cast<int>(i); // Shouldn't compile. (Does with GCC)
There are cases were the compiler may do constant folding - some values can be computed at compile time but don't have to be.
int i = 0;
for (int j = 1; j != 10; ++j)
i += j;
return i;
The compiler can completely eliminate the loop and initialize i with 55 (or simply return 55 and eliminate i too) as long as the behavior stays the same. This is known as the as-if rule.
It means that the program doesn't need to run in order to compute the constant. For example:
int num = 4;
You need these values in order for the compiler to place the variables in symbol tables, where they can be referenced by the program and used. In the case of constants, the compiler symbolizes constants as values that cannot be changed. So if you declare a constant as something that is determined at run-time, it will not work, because if a constant is undefined at compile-time it stays undefined. I hope this makes sense.
Related
Sometimes there is a need to enforce computation of some expression at compile time.
E.g. if we have f(/*some args*/) function that is constexpr, it can be computed both at compile and run time. But if we want to force it to be compile-time one way is to assign its value to constexpr constant:
constexpr auto x = f(/*some args*/);
But what to do if we want it to be used as a part of complex expression, whole expression can be run-time value, but it is interesting sometimes to compute some parts of expression at compile time if it gives some noticeable performance improvements (or for other reasons stated below). E.g. expression:
int x = 123;
int y = x + 456 * f(/*some args*/);
In the expression above one way to enforce compile time computation of f is to use std::integral_constant:
int x = 123;
int y = x + 456 * std::integral_constant<int, f(/*some args*/)>::value;
But maybe there is some shorter way to do it in standard C++? E.g. maybe there is a kind of constexpr operator: 456 * constexpr(f(/*some args*/))?
The shortest form I figured out:
template <auto V>
constexpr inline auto ce = V;
int y = x + 456 * ce<f(/*some args*/)>;
This solution is nice, but maybe there is already similar to ce<...> templated constant inside standard C++ library? So that I don't invent new classes/functions if there are some standard ones. Because current standard class std::integral_constant needs specifying type as first argument and also needs ::value, which is not very short way to write.
Why do I bother about enforcing compile time? Because of next code:
Try it online!
struct S {
consteval bool has42() const {
return i == 42;
}
int i = 0;
};
template <auto V>
constexpr inline auto ce = V;
int main() {
constexpr S s;
int z = 10;
auto x = z + int(ce<s.has42()>); // OK
auto y = z + int(s.has42()); // Compile error
}
Code above works perfectly well under CLang, but under latest MSVC line with auto y.... doesn't compile with error error C7595: 'S::has42': call to immediate function is not a constant expression. Line with auto x..... compiles correctly on both CLang/MSVC due to use of ce<...> which enforces compile time computation.
Seems that MSVC supposes all expressions to be run-time values if they are not stated as constexpr explicitly. Of cause although they are marked as run-time values still compiler does all optimizations and probably compile time code will not be different from run-time.
This question already has answers here:
When does a constexpr function get evaluated at compile time?
(2 answers)
Closed 2 years ago.
This code, when compiled with g++ -O3, does not seem to evaluate get_fibonacci(50) at compile time - as it runs for a very long time.
#include <iostream>
constexpr long long get_fibonacci(int num){
if(num == 1 || num == 2){return 1;}
return get_fibonacci(num - 1) + get_fibonacci(num - 2);
}
int main()
{
std::cout << get_fibonacci(50) << std::endl;
}
Replacing the code with
#include <iostream>
constexpr long long get_fibonacci(int num){
if(num == 1 || num == 2){return 1;}
return get_fibonacci(num - 1) + get_fibonacci(num - 2);
}
int main()
{
long long num = get_fibonacci(50);
std::cout << num << std::endl;
}
worked perfectly fine. I don't know exactly why this is occurring, but my guess is that get_fibonacci(50) is not evaluated at compile-time in the first scenario because items given std::cout are evaluated at runtime. Is my reasoning correct, or is something else happening? Can somebody please point me in the right direction?
Actually, both versions of your code do not have the Fibonnaci number computed at compile-time, with typical compilers and compilation flags. But, interestingly enough, if you reduce the 50 to be, say, 30, both versions of your program do have the compile-time evaluation.
Proof: GodBolt
At the link, your first program is compiled and run first with 50 as the argument to get_fibbonacci(), then with 30, using GCC 10.2 and clang 11.0.
What you're seeing is the limits of the compiler's willingness to evaluate code at compile-time. Both compilers engage in the recursive evaluation at compile time - until a certain depth, or certain evaluation time cap, has elapsed. They then give up and leave it for run-time evaluation.
I don't know exactly why this is occurring, but my guess is that get_fibonacci(50) is not evaluated at compile-time in the first scenario because items given std::cout are evaluated at runtime
Your function can be computed compile-time, because receive a compile-time know value (50), but can also computed run-time, because the returned value is send to standard output so it's used run-time.
It's a gray area where the compiler can choose both solutions.
To impose (ignoring the as-if rule) the compile-time computation, you can place the returned value in a place where the value is required compile-time.
For example, in a template parameter, in your first example
std::cout << std::integral_constant<long long, get_fibonacci(50)>::value
<< std::endl;
or in a constexpr variable, in your second example
constexpr long long num = get_fibonacci(50);
But remember there is the "as-if rule", so the compiler (in this case, also using constexpr or std::integral_constant) can select the run-time solution because this "do not change the observable behavior of the program".
Assign to a constexpr to get the compiler to spit out an error message
constexpr auto val = get_fibonacci(50);
constexpr functions are evaluated at compile time only in constexpr context, which includes assignment to constexpr variables, template parameter, array size...
Regular function/operator call is not such context.
std::cout << get_fibonacci(50);
is done at runtime.
Now, compiler might optimize any (constexpr or not, inline or not) functions with the as-if rule, resulting in a constant, a simpler loop, ...
What assurances do I have that a core constant expression (as in [expr.const].2) possibly containing constexpr function calls will actually be evaluated at compile time and on which conditions does this depend?
The introduction of constexpr implicitly promises runtime performance improvements by moving computations into the translation stage (compile time).
However, the standard does not (and presumably cannot) mandate what code a compiler produces. (See [expr.const] and [dcl.constexpr]).
These two points appear to be at odds with each other.
Under which circumstances can one rely on the compiler resolving a core constant expression (which might contain an arbitrarily complicated computation) at compile time rather than deferring it to runtime?
At least under -O0 gcc appears to actually emit code and call for a constexpr function. Under -O1 and up it doesn't.
Do we have to resort to trickery such as this, that forces the constexpr through the template system:
template <auto V>
struct compile_time_h { static constexpr auto value = V; };
template <auto V>
inline constexpr auto compile_time = compile_time_h<V>::value;
constexpr int f(int x) { return x; }
int main() {
for (int x = 0; x < compile_time<f(42)>; ++x) {}
}
When a constexpr function is called and the output is assigned to a constexpr variable, it will always be run at compiletime.
Here's a minimal example:
// Compile with -std=c++14 or later
constexpr int fib(int n) {
int f0 = 0;
int f1 = 1;
for(int i = 0; i < n; i++) {
int hold = f0 + f1;
f0 = f1;
f1 = hold;
}
return f0;
}
int main() {
constexpr int blarg = fib(10);
return blarg;
}
When compiled at -O0, gcc outputs the following assembly for main:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 55
mov eax, 55
pop rbp
ret
Despite all optimization being turned off, there's never any call to fib in the main function itself.
This applies going all the way back to C++11, however in C++11 the fib function would have to be re-written to use conversion to avoid the use of mutable variables.
Why does the compiler include the assembly for fib in the executable sometimes? A constexpr function can be used at runtime, and when invoked at runtime it will behave like a regular function.
Used properly, constexpr can provide some performance benefits in specific cases, but the push to make everything constexpr is more about writing code that the compiler can check for Undefined Behavior.
What's an example of constexpr providing performance benefits? When implementing a function like std::visit, you need to create a lookup table of function pointers. Creating the lookup table every time std::visit is called would be costly, and assigning the lookup table to a static local variable would still result in measurable overhead because the program has to check if that variable's been initialized every time the function is run.
Thankfully, you can make the lookup table constexpr, and the compiler will actually inline the lookup table into the assembly code for the function so that the contents of the lookup table is significantly more likely to be inside the instruction cache when std::visit is run.
Does C++20 provide any mechanisms for guaranteeing that something runs at compiletime?
If a function is consteval, then the standard specifies that every call to the function must produce a compile-time constant.
This can be trivially used to force the compile-time evaluation of any constexpr function:
template<class T>
consteval T run_at_compiletime(T value) {
return value;
}
Anything given as a parameter to run_at_compiletime must be evaluated at compile-time:
constexpr int fib(int n) {
int f0 = 0;
int f1 = 1;
for(int i = 0; i < n; i++) {
int hold = f0 + f1;
f0 = f1;
f1 = hold;
}
return f0;
}
int main() {
// fib(10) will definitely run at compile time
return run_at_compiletime(fib(10));
}
Never; the C++ standard permits almost the entire compilation to occur at "runtime". Some diagnostics have to be done at compile time, but nothing prevents insanity on the part of the compiler.
Your binary could be a copy of the compiler with your source code appended, and C++ wouldn't say the compiler did anything wrong.
What you are looking at is a QoI - Quality of Implrmentation - issue.
In practice, constexpr variables tend to be compile time computed, and template parameters are always compile time computed.
consteval can also be used to markup functions.
Wondering if the following surprises anyone, as it did me? Alex Allain's article here on using constexpr shows the following factorial example:
constexpr factorial (int n)
{
return n > 0 ? n * factorial( n - 1 ) : 1;
}
And states:
Now you can use factorial(2) and when the compiler sees it, it can
optimize away the call and make the calculation entirely at compile
time.
I tried this in VS2015 in Release mode with full optimizations on (/Ox) and stepped through the code in the debugger viewing the assembly and saw that the factorial calculation was not done at compilation.
Using GCC v5.4.0 with --std=C++14, I must use /O2 or /O3 before the calculation is performed at compile time. I was surprised thought that using just /O the calculation did not occur at compilation time.
Main main question is: Why is VS2015 not performing this calculation at compilation time?
It depends on the context of the function call.
For example, the following obviously could never be calculated at compile time:
int x;
std::cin >> x;
std::cout << factorial(x);
On the other hand, this context would require the answer at compile time:
class Foo {
int x[factorial(4)];
};
constexpr functions are only guaranteed to be evaluated at compile time if they are called from a constexpr context; otherwise it is up to the compiler to choose whether or not to eval at compile time (assuming such an optimization is possible, again, depending on the context).
You have to use it in const expression, as:
constexpr auto res = factorial(2);
else computation can be done at runtime.
constexpr is neither necessary nor sufficient to compile time evaluation of a function.
It's not sufficient, even aside from the fact that the arguments obviously also have to be constant expressions. Even if that is true, a conforming compiler does not have to evaluate it at compile time. It only has to be evaluated at compile time if it is in a constexpr context. Such as, assigning the result of the computation to a constexpr variable, or using the value as an array size, or as a non-type template parameter.
The other point, is that the compiler is completely capable of evaluating things at compile time, even without constexpr. There is a lot of confusion about this, and it's not clear why. compile time evaluation of constexpr functions fundamentally just boils down to constant propagation, and compilers have been doing this optimization since forever: https://godbolt.org/g/Sy214U.
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n-1);
}
int foo() { return factorial(5); }
On gcc 6.3 with O3 (and 14) yields:
foo():
mov eax, 120
ret
In essence, outside of the specific case where you absolutely force compile time evaluation by assigning a constexpr function to another constexpr variable, compile time evaluation has more to do with the quality of your optimizer than the standard.
I found this code, and wondering whether I should really implement something like this in my real project or not.
And things confusing me are
It will take more compile time, but I should not bother about
compile time at the cost of run time.
And what if N gets really a very big number then? Is there any file
size limit in source code?
or its something good to know, not to implement?
#include <iostream>
using namespace std;
template<int N>
class Factorial {
public:
static const int value = N * Factorial<N-1>::value;
};
template <>
class Factorial<1> {
public:
static const int value = 1;
};
int main() {
Factorial<5L> f;
cout << "5! = " << f.value << endl;
}
outPut:
5! = 120
slight modification in question, as i was playing with the code, found that
Factorial<12> f1; // works
Factorial<13> f2; // doesn't work
error:
undefined reference to `Factorial<13>::value'
is it like, can go up-to 12 depth not further?
The answer to 1 is that it depends, template meta programming essentially involves a trade-off between doing the calculation at compile-time with the benefit that it does not have to be done at run-time. In general this technique can lead to hard to read and maintain code. So the answer ultimately depends on your need for faster run-time performance over slower compiler times and possibly harder to maintain code.
The article Want speed? Use constexpr meta-programming! explains how in modern C++ you can use constexpr functions many times as a replacement for template meta programming. This in general leads to code that is more readable and perhaps faster. Compare this template meta programming method to the constexpr example:
constexpr int factorial( int n )
{
return ( n == 0 ? 1 : n*factorial(n-1) ) ;
}
which is more concise, readable and will be executed at compile time for arguments that are constant expressions although as the linked answer explains the standard does not actually say it must be but in practice current implementations definitely do.
It is also worth noting that since the result will quickly overflow value that another advantage of constexpr is that undefined behavior is not a valid constant expression and at least the current implementations of gcc and clang will turn undefined behavior within a constexpr into an error for most cases. For example:
constexpr int n = factorial(13) ;
for me generates the following error:
error: constexpr variable 'n' must be initialized by a constant expression
constexpr int n = factorial(13) ;
^ ~~~~~~~~~~~~~
note: value 6227020800 is outside the range of representable values of type 'int'
return ( n == 0 ? 1 : n*factorial(n-1) ) ;
^
This is also why you example:
Factorial<13> f2;
fails because a constant expression is required and gcc 4.9 gives a useful error:
error: overflow in constant expression [-fpermissive]
static const int value = N * Factorial<N-1>::value;
^
although older versions of gcc give you the less than helpful error you are seeing.
For question 2, compilers have a template recursion limit, which can usually be configured but eventually you will run out of system resources. For example the flag for gcc would be -ftemplate-depth=n:
Set the maximum instantiation depth for template classes to n. A limit
on the template instantiation depth is needed to detect endless
recursions during template class instantiation. ANSI/ISO C++
conforming programs must not rely on a maximum depth greater than 17
(changed to 1024 in C++11). The default value is 900, as the compiler
can run out of stack space before hitting 1024 in some situations.
As in your specific problem you will need to worry about signed integer overflow, which is undefined behavior before you have system resource issues.