我正在学习C ++模板元编程。我想知道以下结构之间的区别。假设阶乘的经典例子。
template <int n>
struct factorial
{
enum { fac = n*factorial<n-1>::fac };
};
factorial<4> ex;
(我们省略终止条件以简洁。)
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>();
4个例子之间有什么区别?特别是,在编译时为每个变体做了什么?
很明显,示例1完全是编译时。我认为第二个也是编译时,但我不确定编译器的作用。第三个也是在编译时?我不确定。
第四个不是编译时间。 在编译时完成了什么?编译器是否为函数创建了代码生成器,其中n是&#34;替换&#34;按常数值?
请纠正我,或加入我的想法。
答案 0 :(得分:2)
4个例子之间有什么区别?特别是,在编译时为每个变体做了什么?
很明显,示例1完全是编译时。
非常正确:factorial<4>
是一种类型,并且在编译时创建/计算类型。因此,在编译时计算4的阶乘。在运行时,类型ex
的对象factorial<4>
被初始化(可以初始化:编译器也可以在编译时自由执行)。
我认为第二个也是编译时,但我不确定编译器的作用。
我没有看到ex1
行。
我想
factorial<4> ex1 = foo1<4>();
或者,从C ++ 11开始,也是
auto ex1 = foo1<4>();
答案与案例(1)完全相同:foo<N>()
返回一个factorial<N>
,它是一个类型,一个类型是创建/计算的编译时间;所以N的阶乘是计算编译时间;运行时调用(可以调用)函数foo1()
并初始化(可以初始化)类型为ex1
的对象factorial<4>
。
第三个也是在编译时?我不确定。
否:它会返回int
,而不是factorial<N>
。它通常计算运行时间(即使编译器可以在编译时自由计算它)。
但是,从C ++ 11开始,您可以将foo2()
写为constexpr
函数,如下所示:
template<int n>
constexpr int foo2(){return n*foo2<n-1>();}
template<>
constexpr int foo2<0>(){return 1;}
constexpr
函数可以在编译时或运行时计算,但是如果将它用于必须在编译时知道的值,则可以在编译时强制计算。
例如,初始化constexpr
变量:
constexpr int ex2 = foo2<4>(); // factorial computed compile time
或者是C型数组的大小:
int a[foo2<4>()]; // factorial computed compile time
用于模板参数:
factorial<foo2()> ex; // factorial of factorial computed compile time
但constexpr
功能可以更简单,更灵活。您可以将其编写为未模板化的递归函数,如下所示[警告:代码未经过测试]
constexpr int foo2 (int n)
{ return n ? n * foo2(n-1) : 1; }
这种方式foo2()
可以计算(在运行时,显然)运行时变量的阶乘值。例如:
for ( auto i = 0 ; i < 5 ; ++i )
std::cout << i << "! = " << foo2(i) << std::endl;
观察foo2()
在编译时仍然可用,但必须在编译时知道值n
。
我的意思是
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)
第四个不是编译时间。编译时做了什么?编译器是否为函数创建了代码生成器,其中n是&#34;替换&#34;按常数值?
右。
但你可以定义它constexpr
(从C ++ 14开始:它太复杂了,不能成为C ++ 11 constexpr
函数),所以它可以在编译时计算出来-time或运行时,必须计算编译时必须在编译时知道该值。
constexpr int ex3 = foo3<4>(); // factorial computed compile time
作为foo2()
,可以定义foo3()
constexpr
接收非模板参数(我重复一遍:从C ++ 14开始)[警告:代码未经过测试< /强>
constexpr int foo3 (int n)
{
int k = 1;
for (int i=2 ; i <= n ; i++ )
k *= i;
return k;
}
或者,以更紧凑的方式:
constexpr int foo3 (int n)
{
int ret = n ? n : 1;
while ( --n > 0 )
ret *= n;
return ret;
}