c ++ - >模板元编程与模板函数

时间:2018-01-12 16:53:06

标签: c++ templates template-meta-programming template-specialization

我正在学习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是&#34;替换&#34;按常数值?

请纠正我,或加入我的想法。

1 个答案:

答案 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;
}