I am learning C++ template meta-programming. I want to know the difference between the following constructs. Suppose the classic example of the factorial.
template <int n>
struct factorial
{
enum { fac = n*factorial<n-1>::fac };
};
factorial<4> ex;
(where we omit the termination conditions for brevity.)
template<int n> factorial<n> foo1(){return factorial<n>();}
template<int n> int foo2(){return n*foo2<n-1>();}
template<> int foo2<0>(){return 1;}
int ex2=foo2<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?
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?
Please correct me, or add to my thoughts.
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<N>()
return a factorial<N>
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<N>
. 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<int n>
constexpr int foo2(){return n*foo2<n-1>();}
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<foo2()> 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;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.