Template factorial function without template specialization - c++

I don't understand the following behavior.
The following code, aimed at computing the factorial at compile time, doesn't even compile:
#include <iostream>
using namespace std;
template<int N>
int f() {
if (N == 1) return 1; // we exit the recursion at 1 instead of 0
return N*f<N-1>();
}
int main() {
cout << f<5>() << endl;
return 0;
}
and throws the following error:
...$ g++ factorial.cpp && ./a.out
factorial.cpp: In instantiation of ‘int f() [with int N = -894]’:
factorial.cpp:7:18: recursively required from ‘int f() [with int N = 4]’
factorial.cpp:7:18: required from ‘int f() [with int N = 5]’
factorial.cpp:15:16: required from here
factorial.cpp:7:18: fatal error: template instantiation depth exceeds maximum of 900 (use ‘-ftemplate-depth=’ to increase the maximum)
7 | return N*f<N-1>();
| ~~~~~~^~
compilation terminated.
whereas, upon adding the specialization for N == 0 (which the template above doesn't even reach),
template<>
int f<0>() {
cout << "Hello, I'm the specialization.\n";
return 1;
}
the code compiles and give the correct output of, even if the specialization is never used,
...$ g++ factorial.cpp && ./a.out
120

The issue here is that your if statement is a run time construct. When you have
int f() {
if (N == 1) return 1; // we exit the recursion at 1 instead of 0
return N*f<N-1>();
}
the f<N-1> is instantiated as it may be called. Even though the if condition will stop it from calling f<0>, the compiler still has to instantiate it since it is part of the function. That means it instantiates f<4>, which instantiates f<3>, which instantiates f<2>, and on and on it will go forever.
The Pre C++17 way to stop this is to use a specialization for 0 which breaks that chain. Starting in C++17 with constexpr if, this is no longer needed. Using
int f() {
if constexpr (N == 1) return 1; // we exit the recursion at 1 instead of 0
else return N*f<N-1>();
}
guarantees that return N*f<N-1>(); won't even exist in the 1 case, so you don't keep going down the instantiation rabbit hole.

The problem is that f<N>() always instantiates f<N-1>() whether that if branch is taken or not. Unless properly terminated, that would create infinite recursion at compile time (i.e. it would attempt instantiating F<0>, then f<-1>, then f<-2> and so on). Obviously you should terminate that recursion somehow.
Apart from constexpr solution and specialization suggested by NathanOliver, you may terminate the recursion explicitly:
template <int N>
inline int f()
{
if (N <= 1)
return 1;
return N * f<(N <= 1) ? N : N - 1>();
}
Mind, this solution is rather poor (the same terminal condition must be repeated twice), I'm writing this answer merely to show that there are always more ways to solve the problem :- )

Related

How to understand constexpr in this example?

I'm trying to understand the meaning of constexpr when applied to functions. In the example below the program compiles and runs but I don't understand how the function sum(int n) can be deduced at compile time as n is not known until run time. I'm using VS 2017 with latest updates.
The program compiles whether constexpr is included or not.
#include <iostream>
constexpr int sum(int n)
{
return (n <= 0) ? 0 : n + sum(n-1);
}
int main()
{
int i;
std::cin >> i;
std::cout << sum(i) << std::endl;
return 0;
}
I expected the compiler to error that sum(int n) is not a constant expression. Or is constepxr just a hint to the compiler like "inline", that it is free to ignore?
I expected the compiler to error that sum(int n) is not a constant expression.
constexpr int sum(int n); means that the function can be evaluated at compile time. It doesn't have to be. You can call it at runtime without any issues, which makes sense to not force programmers to duplicate code when they need identical functionality at runtime as well as at compile time.
With C++20, you'll be able to trigger the error you were expecting by qualifying the function with the new keyword consteval instead of constexpr.
consteval int sum(int n)
{
// As before...
}
int i;
// determine i at runtime...
sum(i); // Error! Not evaluated at compile time.
You can have a look at P1073 for this feature. This proposal has been approved for the next standard.
The constexpr keyword says that the function must be evaluated at compile time, if it's called in a constexpr context.
Consider:
constexpr int sum(int n)
{
return (n <= 0) ? 0 : n + sum(n-1);
}
int main()
{
int i;
std::cin >> i;
constexpr int s1 = sum(4); // OK, evaluated at compile time
int s2 = sum(i); // OK, evaluated at run time
constexpr int s3 = sum(i); // Error, i cannot be evaluated at compile time
int s4 = sum(4); // OK, execution time depends on the compiler's mood
}
Here, s3 is constexpr and so it's initializer needs to be evaluated at compile time. Hence the error.
If this feature weren't there, you would have to write two versions of your function, one for compile time use and the other for run-time use.
See it yourself on the Compiler Explorer.
Also note that constexpr implies inline for functions.
Here is what my perspective is
constexpr, ensures that the constant must be a compile-time constant
Thus
constexpr double pi (3.5); // is Okay because resolution is at compile time
and this
// Should be Okay be cause the resolution of **sum** is at compiletime
// evaluation however is run-time dependent
constexpr int sum(int n)
{
return (n <= 0) ? 0 : n + sum(n-1);
}
Another example is something like this
constexpr int compute(int x) {
return x+1;
}
int foo[compute(15)];

Comparing constexpr function parameter in constexpr-if condition causes error

I'm trying to compare a function parameter inside a constexpr-if statement.
Here is a simple example:
constexpr bool test_int(const int i) {
if constexpr(i == 5) { return true; }
else { return false; }
}
However, when I compile this with GCC 7 with the following flags:
g++-7 -std=c++1z test.cpp -o test
I get the following error message:
test.cpp: In function 'constexpr bool test_int(int)':
test.cpp:3:21: error: 'i' is not a constant expression
if constexpr(i == 5) { return true; }
However, if I replace test_int with a different function:
constexpr bool test_int_no_if(const int i) { return (i == 5); }
Then the following code compiles with no errors:
int main() {
constexpr int i = 5;
static_assert(test_int_no_if(i));
return 0;
}
I don't understand why the constexpr-if version fails to compile, especially since the static_assert works just fine.
Any advice on this would be appreciated.
Thanks!
From constexpr if:
In a constexpr if statement, the value of condition must be a
contextually converted constant expression of type bool.
Then, from constant expression:
Defines an expression that can be evaluated at compile time.
Obviously, i == 5 is not a constant expression, because i is a function parameter which is evaluated at run time. That is why the compiler complains.
When you use a function:
constexpr bool test_int_no_if(const int i) { return (i == 5); }
then it might be evaluated during the compile time depending on whether it's parameter is known at compile time or not.
If i is defined like:
constexpr int i = 5;
then the value of i is known during the compile time and test_int_no_if might be evaluated during the compile too making it possible to call it inside static_assert.
Also note, that marking function parameter as const does not make it a compile time constant. It just means that you cannot change the parameter inside the function.
A constexpr function can be called with non-constexpr arguments, in which case it behaves like a normal function, so the code must still compile as if it were not constexpr.
In short, there's nothing in test_int_no_if that depends on i being constexpr, while in test_int(), there is. ("constexpr if" only works with compile time expressions.)

conditional evaluation of functions

Ok, this might be a stupid question, but I completely don't understand chapter 12.1.6.2 - conditional evaluation under constexpr functions of the c++ programming language. This is the whole very short text.
A branch of a conditional expression that is not taken in a constexpr
function is not evaluated. This implies that a branch not taken can
require run-time evaluation. For example:
constexpr int check(int i)
{
return (low<=i && i<high) ? i : throw out_of_range();
}
constexpr int low = 0;
constexpr int high = 99;
// ...
constexpr int val = check(f(x,y,z));
You might imagine low and high to be configuration parameters that are
known at compile time, but not at design time, and that f(x,y,z)
computes some implementation-dependent value.
Source for context
I tried running the code above to try to more understand the explanation but I'm getting an error. Can someone provide a clearer explanation?
Edit: I created a program to test this:
#include<iostream>
#include<stdexcept>
using namespace std;
constexpr int low = 0;
constexpr int high = 99;
constexpr int check(int i) {
return (low<=i && i<high) ? i : throw out_of_range();
}
constexpr int f(int x, int y, int z) {
return x*y*z;
}
int main() {
constexpr int val = check(f(2,2,2));
cout << val << '\n';
}
It won't run:
no matching function for call to 'std::out_of_range::out_of_range()' //I'm really surprised at this
return (low<=i && i<high) ? i : throw out_of_range();
error: body of constexpr function 'constexpr int check(int)' not a return-statement
}
error: 'constexpr int check(int)' called in a constant expression
constexpr int val = check(f(2,2,2));
It means that a conditional branch in a constexpr function is allowed to use non-constant expressions (i.e. ones requiring run-time evaluation, such as throwing exceptions) as long as that branch is never taken when the function is called in a constant expression context.
So it's OK to call check to initialized the constexpr variable val, as long as the arguments to the function are constant expressions, and the condition (low<=i && i<high) is true.
If the arguments are not constants, the function call is not a constant expression and so can't initialize a constexpr variable.
If the condition is false the function needs to take the false branch, which needs to throw an exception, which requires run-time evaluation, so the function is not a constant expression, and so cannot initialize a constexpr variable.
When the argument is a constant expression, the compiler knows at compile-time whether the branch will be taken, so as long as the condition is true it can completely ignore the false branch, and doesn't complain that throwing exceptions is impossible at compile-time.
When the function is called at run-time, it can have any arguments, and the false branch can be taken and the throw will be evaluated as for a normal (non-constexpr) function.

C++11 constexpr function compiler error with ternary conditional operator (?:)

What is wrong with this piece of code?
#include <iostream>
template<unsigned int N, unsigned int P=0>
constexpr unsigned int Log2() {
return (N <= 1) ? P : Log2<N/2,P+1>();
}
int main()
{
std::cout << "Log2(8) = " << Log2<8>() << std::endl;
return 0;
}
When compiling with gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5), I get the following error:
log2.cpp: In function ‘constexpr unsigned int Log2() [with unsigned int N = 0u, unsigned int P = 1023u]’:
log2.cpp:5:38: error: template instantiation depth exceeds maximum of 1024 (use -ftemplate-depth= to increase the maximum) instantiating ‘constexpr unsigned int Log2() [with unsigned int N = 0u, unsigned int P = 1024u]’
log2.cpp:5:38: recursively instantiated from ‘constexpr unsigned int Log2() [with unsigned int N = 4u, unsigned int P = 1u]’
log2.cpp:5:38: instantiated from ‘constexpr unsigned int Log2() [with unsigned int N = 8u, unsigned int P = 0u]’
log2.cpp:10:37: instantiated from here
Constexpr doesn't work that way.
Simply put, constexpr functions must be available as runtime functions too. Imagine you took the constexpr off the function. Then think about why it cannot possibly work.
The reason is that the compiler has to instantiate the body of the function completely; it cannot decide based on the condition in the ?: not to instantiate one side. So it always has to instantiate the recursive call, leading to infinite recursion.
In any case, you're using constexpr wrong. You're using the old template metaprogramming calculation technique (passing stuff as template parameters) when constexpr was intended to replace this. Just use normal parameters.
constexpr unsigned Log2(unsigned n, unsigned p = 0) {
return (n <= 1) ? p : Log2(n / 2, p + 1);
}
std::cout << "Log2(8) = " << Log2(8) << std::endl;
Edit: I'll try to elaborate on how this works.
When the compiler encounters your code, it parses the template function and stores it in template form. (How this works differs between compilers.) So far, all is fine.
Next, in main, the compiler sees the call Log2<8>(). It sees that it has to instantiate the template, so it goes ahead and does exactly that: it instantiates Log2<8, 0>. The body of the function template is this:
return (N <= 1) ? P : Log2<N/2,P+1>();
OK, the compiler sees this, but it doesn't try to evaluate it. Why would it? It's currently instantiating a template, not calculating a value. It just substitutes the values supplied:
return (8 <= 1) ? 0 : Log2<8/2,0+1>();
Huh, there's another template instantiation here. It doesn't matter that it's in a conditional expression, or that the left hand side could be known. Template instantiation must be complete. So it goes ahead and calculates the values for the new instantiation and then instantiates Log2<4, 1>:
return (4 <= 1) ? 1 : Log2<4/2,1+1>();
And the game begins again. There's a template instantiation in there, and it's Log2<2, 2>:
return (2 <= 1) ? 2 : Log2<2/2,2+1>();
And again, Log2<1,3>():
return (1 <= 1) ? 3 : Log2<1/2,3+1>();
Did I mention that the compiler doesn't care about the semantic meaning of this stuff? It's just yet another template to instantiate: Log2<0,4>:
return (0 <= 1) ? 4 : Log2<0/2,4+1>();
And then Log2<0,5>:
return (0 <= 1) ? 5 : Log2<0/2,5+1>();
And so on, and so on. At some point the compiler realizes that it never stops, and gives up. But at no point does it say, "Wait, the condition of that ternary operator is false, I don't need to instantiate the right-hand side." That's because the C++ standard doesn't allow it to. The function body must be instantiated completely.
Now look at my solution. There's no template. There's just a function. The compiler sees it and goes, "Hey, here's a function. Awesome, let me put in a call to that function here." And then at some point (it might be immediately, it might be a lot later, depending on the compiler), it might (but is not forced to, in this case) say, "Hey, wait, this function is constexpr and I know the parameter values, let me evaluate that." Now it goes ahead and evaluates Log2(8, 0). Remember the body:
return (n <= 1) ? p : Log2(n / 2, p + 1);
"OK", the compiler says, "I just want to know what this function returns. Let's see, 8 <= 1 is false, so look at the right side. Log2(4, 1), huh? Let me look at that. OK, 4 <= 1 is also false, so it must be Log2(2, 2). What's that, 2 <= 1? Also false, so it's Log2(1, 3). Hey, 1 <= 1 is true, so let me take that 3 and return it. All the way up the call stack."
So it comes up with the answer 3. It doesn't go into endless recursion because it's evaluating the function with full knowledge of values and semantics, not just stupidly building up ASTs.
I hope that helps.
As others already said: The compiler won't evaluate a conditional expression such as if/else or a ternary operator ?:. However, there is still a way to make this conditional expression compile time:
#include <cstddef> // size_t is shorter than unsigned int, it's a matter of taste in this case
#include <iostream> // to show our results
#include <type_traits> // needed for the mighty std::enable_if
template<size_t N, size_t P = 0>
constexpr typename std::enable_if<(N <= 1), size_t>::type Log2()
{
return P;
}
template<size_t N, size_t P = 0>
constexpr typename std::enable_if<!(N <= 1), size_t>::type Log2()
{
return Log2<N / 2, P + 1>();
}
int main()
{
std::cout << Log2<1>() << "\n";
std::cout << Log2<2>() << "\n";
std::cout << Log2<4>() << "\n";
std::cout << Log2<8>() << "\n";
std::cout << Log2<16>() << "\n";
}
What this does is rather obvious: If N <= 1, the first branch should be evaluated, thus Log2<0, P>() and Log2<1, P>() should evaluate to P. If N <= 1, the upper method is enabled as this function header is valid. For everything else, i.e. N >= 2 or !(N <= 1), we need to recurse, which is done by the second method.

Recursive template for compile-time bit mask

I'm trying to create a compile-time bit mask using metaprograming techniques, my idea is to create something like this:
unsigned int Mask3 = Mask<2>(); // value = 0x03 = b00000000000000000000000000000011
unsigned int Mask3 = Mask<3>(); // value = 0x07 = b00000000000000000000000000000111
unsigned int Mask3 = Mask<7>(); // value = 0x7F = b00000000000000000000000001111111
The code that I'm trying is this:
template <const unsigned int N> const unsigned int Mask()
{
if (N <= 1)
{
return 1;
}
else
{
return ((1 << N) | Mask<N - 1>());
}
}
return 1;
But it result in tons pairs of warnings:
warning C4554: '<<' : check operator precedence for possible error
warning C4293: '<<' : shift count negative or too big
And in the end, the compile error:
error C1202: recursive type or function dependency context too complex.
So, I deduce that the recursivity never ends and falls into a compiler infinite loop but I'm don't understanding WHY.
As has already been pointed out, you're depending on a runtime check to
stop a compile time recursion, which can't work. More importantly,
perhaps, for what you want to do, is that you're defining a function,
which has no value until you call it. So even after you stop the
recursion with a specialization, you still have a nested sequence of
functions, which will be called at runtime.
If you want full compile time evaluation, you must define a static data
member of a class template, since that's the only way a compile time
constant can appear in a template. Something like:
template <unsigned int N>
struct Mask
{
static unsigned int const value = (1 << (N - 1)) | Mask<N - 1>::value;
};
template <>
struct Mask<0>
{
static unsigned int const value = 0;
};
(I've also corrected the numerical values you got wrong.)
Of course, you don't need anything this complicated. The following
should do the trick:
template <unsigned int N>
struct Mask
{
static unsigned int const value = (1 << (N + 1)) - 1;
};
template <>
struct Mask<0>
{
static unsigned int const value = 0;
};
(You still need the specialization for 0. Otherwise, 0 means all bits
set.)
Finally, of course: to access the value, you need to write something
like Mask<3>::value. (You might want to wrap this in a macro.)
It doesn't need to be recursive. This should work just fine :
template <const unsigned int N> const unsigned int Mask()
{
return ((1 << N) - 1);
}
It doesn't even need to be a template really. An (inlined) function is ok.
Note that if you want to support any value of N, specifically N >= sizeof(unsigned int) * CHAR_BIT, you probably want to treat those as a special case.
A template is created at compile time, but you are relying on run time behavior to stop the recursion.
For example, if you instantiate Mask<2>, it is going to use Mask<1>, which is going to use Mask<0>, which is going to use Mask<-1>, etc.
You have a runtime check for N being <= 1, but this doesn't help when it's compiling. It still creates an infinite sequence of functions.
To blunt template instantiation recursion you need to introduce one explicit specialization:
template <0> const unsigned int Mask()
{
return 1;
}
Your recursion never ends, because compiler tries to generate template implementation for both if-branches. So, when it generates Mask<0> it also generates Mask<0xffffffff> and so on
C++11 -- no recursion or templates:
constexpr unsigned mask(unsigned N) { return unsigned(~(-1<<N)); }
So far the answers only addressed the second error (C1202), but you asked more than that.
Warning C4554 is caused by a Microsoft compiler bug involving template parameters and the << operator. So, (1 << N) generates a warning. If N were an ordinary parameter, there would be no warning of course.
The very simple workaround is to use (1 << (N)) instead of (1 << N), and C4554 goes away!