Home c++ -> template meta programming vs template functions

# c++ -> template meta programming vs template functions

LeastSquaresWonderer
1#
LeastSquaresWonderer Published in 2018-01-12 16:53:06Z

I am learning C++ template meta-programming. I want to know the difference between the following constructs. Suppose the classic example of the factorial.

# Example 1

template <int n>
struct factorial
{
enum { fac = n*factorial<n-1>::fac };
};
factorial<4> ex;


(where we omit the termination conditions for brevity.)

# Example 2

template<int n> factorial<n> foo1(){return factorial<n>();}


# Example 3

template<int n> int foo2(){return n*foo2<n-1>();}
template<> int foo2<0>(){return 1;}
int ex2=foo2<4>()


# Example 4

template<int n>
int foo3(){
int k=1;
for(int i=2;i<=n;i++) k*=i;
return k;
}
int ex3=foo3<4>();


What are the differences between the 4 examples? In particular, what is done at compile time for each variant?

# My Thoughts

It's clear that example 1 is completely compile-time. The second, I think, is also compile-time, but I'm not sure what the compiler does. The third is also at compile time? I'm not certain.

The fourth isn't compile-time. What is done at compile time? Does the compiler create a code-generator for the function, where n is "replaced" by the constant value?

 What are the differences between the 4 examples? In particular, what is done at compile time for each variant? It's clear that example 1 is completely compile-time. Quite right: the factorial<4> is a type, and a type is created/computed at compile-time. So the factorial of 4 is computed at compile-time. At run-time, the object ex of type factorial<4> is initialized (can be initialized: the compiler is free do it at compile-time also). The second, I think, is also compile-time, but I'm not sure what the compiler does. I don't see the ex1 row. I suppose factorial<4> ex1 = foo1<4>();  or, starting from C++11, also auto ex1 = foo1<4>();  The answer is quite as for case (1): foo() return a factorial that is a type and a type is created/computed compile time; so the factorial of N is computed compile time; run time is called (can be called) the function foo1() and is initialized (can be initialized) the object ex1 of type factorial<4>. The third is also at compile time? I'm not certain. No: It returns an int, not a factorial. It's usually computed run-time (even if the compiler is free to compute it at compile-time). But, starting from C++11, you can write foo2() as a constexpr function, like so: template constexpr int foo2(){return n*foo2();} template<> constexpr int foo2<0>(){return 1;}  A constexpr function can be computed compile-time or run-time, but you can force its computation at compile-time if you use it for a value that must be known at compile-time. By example, initializing a constexpr variable: constexpr int ex2 = foo2<4>(); // factorial computed compile time  or for a size of a C-style array: int a[foo2<4>()]; // factorial computed compile time  of for a template argument: factorial ex; // factorial of factorial computed compile time  But the constexpr function can be simpler and more flexible. You can write it as an untemplated recursive function as follows [Caution: code not tested] constexpr int foo2 (int n) { return n ? n * foo2(n-1) : 1; }  This way foo2() can compute (at run-time, obviously) factorial values for run-time variables. For example: for ( auto i = 0 ; i < 5 ; ++i ) std::cout << i << "! = " << foo2(i) << std::endl;  Observe that foo2() remains usable at compile-time, but the value n must be known at compile-time. I mean constexpr int i1 = 4; // i1 is usable compile time int i2 = 4; // i2 isn't usable compile time int f1 = foo2(4); // OK: computed run time (f1 isn't constexpr) int f2 = foo2(i1); // OK: computed run time (f2 isn't constexpr) int f3 = foo2(i2); // OK: computed run time (+ i2 ins't constexpr) constexpr f4 = foo2(4); // OK: computed compile time constexpr f5 = foo2(i1); // OK: computed compile time constexpr f6 = foo2(i2); // compilation error! (i2 isn't constexpr)  The fourth isn't compile-time. What is done at compile time? Does the compiler create a code-generator for the function, where n is "replaced" by the constant value? Right. But you can define it constexpr (starting from C++14: it's too complex to be a C++11 constexpr function), so it can be computed at compile-time or run-time and must be computed compile-time when the value must be known at compile-time. constexpr int ex3 = foo3<4>(); // factorial computed compile time  As foo2(), foo3() can be defined constexpr receiving a not-template parameter (I repeat: starting from C++14) [Caution: code not tested] constexpr int foo3 (int n) { int k = 1; for (int i=2 ; i <= n ; i++ ) k *= i; return k; }  or, in a more compact way: constexpr int foo3 (int n) { int ret = n ? n : 1; while ( --n > 0 ) ret *= n; return ret; }