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

标签 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 被常量值“替换”?

请指正,或补充我的想法。

最佳答案

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/

相关文章:

c++ - OPC连接建立

c++ - 为类对象创建线程

c++ - 第一个归因的段错误(C++ 中的类)

c++ - 与 `if constexpr` 一起使用的编译时间消息(在预处理器之后)

c++ - 确定模板中结构或元组的成员偏移量

C++ 创建通用 std::bind 的 std::packaged_task

c++ - C++中的静态变量和普通变量有什么区别?

c++ - 为什么这个静态断言不起作用?

templates - 使用 Go 的 html/模板免于转义的白名单标签

c++ - 返回多个值中最小值的 min() 的简洁实现