我正在学习 C++ 模板元编程。我想知道以下构造之间的区别。假设阶乘的经典例子。
例子1
template <int n>
struct factorial
{
enum { fac = n*factorial<n-1>::fac };
};
factorial<4> ex;
(为简洁起见,我们省略了终止条件。)
例子2
template<int n> factorial<n> foo1(){return factorial<n>();}
示例 3
template<int n> int foo2(){return n*foo2<n-1>();}
template<> int foo2<0>(){return 1;}
int ex2=foo2<4>()
例子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 被常量值“替换”?
请指正,或补充我的想法。
最佳答案
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.
非常正确:factorial<4>
是一种类型,类型是在编译时创建/计算的。所以 4 的阶乘是在编译时计算的。在运行时,对象 ex
类型 factorial<4>
已初始化(可以初始化:编译器也可以在编译时免费执行此操作)。
The second, I think, is also compile-time, but I'm not sure what the compiler does.
我没有看到 ex1
行。
我想
factorial<4> ex1 = foo1<4>();
或者,从 C++11 开始,也是
auto ex1 = foo1<4>();
答案与案例(1)完全相同:foo<N>()
返回 factorial<N>
那是一个类型,一个类型是在编译时创建/计算的;所以 N 的阶乘是计算编译时间;运行时调用(可调用)函数foo1()
并被初始化(可初始化)对象ex1
类型 factorial<4>
.
The third is also at compile time? I'm not certain.
否:它返回 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;}
A 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)
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?
没错。
但是你可以定义它constexpr
(从 C++14 开始:它太复杂了,不能成为 C++11 constexpr
函数),因此它可以在编译时或运行时计算,并且必须在编译时计算-必须在编译时知道该值的时间。
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;
}
关于c++ -> 模板元编程与模板函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48230687/