为什么使用非类型模板参数?

Ziy*_*her 4 c++ templates arguments non-type

我读了很多问题和答案,但这个问题引起了我的注意; 它和它的答案是有帮助的,但我仍然觉得我不完全理解使用非类型模板参数背后的用法和理论.它们提供了许多有用的例子,说明它何时被使用,但没有一个真正阐明非类型模板参数背后的理论.

我有兴趣在示例中不具体知道,但更广泛地说,为什么我倾向于使用非类型模板参数而不仅仅是常规函数参数.

我知道它们是在编译时确定的,并且每个新调用都会创建一个具有非类型模板参数的确定值的新函数.那么,为什么我只想在函数中输入我想要的参数并且具有相同的结果(假设只有一个编译函数)时,我想要创建相同函数的许多不同实例.

从本质上说,为什么要我倾向于做#1,而不是#2,如根据的最后一节这个页面cplusplus.com

#1:

template <class T, int N>
T fixed_multiply (T val)
{
  return val * N;
}

int main() {
  std::cout << fixed_multiply<int,2>(10) << '\n';
  std::cout << fixed_multiply<int,3>(10) << '\n';
}
Run Code Online (Sandbox Code Playgroud)

#2:

template <class T>
T fixed_multiply (T val, int N)
{
  return val * N;
}

int main() {
  std::cout << fixed_multiply<int>(10, 2) << '\n';
  std::cout << fixed_multiply<int>(10, 3) << '\n';
}
Run Code Online (Sandbox Code Playgroud)

此外,是否会有任何性能优势?是否有任何常见的应用程序可以从使用非类型模板参数中受益,或者这是一个在特定应用程序中使用的高技术参数,以产生特定的结果?

编辑:由于某种原因,这被标记为重复,第一段解释了我的类似问题的原因.

Alw*_*sed 8

当您希望(或需要)编译器进行某些编译时优化同时保持代码良好因素时,它们非常有用.我将简要列举几个例子:

分支消除

void doSomething(bool flag) {
  if (flag) {
    //whatever
  }
}

template <bool flag>
void doSomething() {
 if (flag) {
   //whatever
}
Run Code Online (Sandbox Code Playgroud)

}

在上面的示例中,如果您flag在调用时始终知道编译时的值doSomething,则可以避免分支的成本而无需手动创建doSomethingTruedoSomethingFalse可能需要重复大量代码的函数.例如,当您想要在网络代码中的发送和接收端之间对代码进行分解时,此技巧非常有用,并且您的代码位于性能关键型堆栈的深处.

避免动态内存管理

void someFunction(size_t size, float* inout) {
  std::vector<float> tmp(size);
  //do something with inout and tmp and then store result in inout
}

template <size_t N>
void someFunction(float *inout) {
  float tmp[N]; //or std::array if you like
  //do something with inout and tmp and store result in inout
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,如果第二个版本卡在一个关键循环中,它将表现得更好.

允许特殊优化

考虑循环:

for (size_t i = 0; i < N; ++i) {
  //some arithmetic
}
Run Code Online (Sandbox Code Playgroud)

如果编译器知道N是4或8,它可能能够用等效的SIMD指令替换你的算法,或者至少比N是运行时参数更智能地展开你的循环.这种事情在GPU编程(CUDA)世界中很常见.

模板元编程

当您使用模板执行一些非平凡的代码生成时,有时您必须在编译时迭代事物.您需要模板非类型参数才能执行此操作.参数包std::tuple是一种常见的情况,但还有更多,很难在这里举例说明.

如果你有一个选择,你可以回答你什么时候做一个模板参数的问题:采用动态(运行时)参数的代码总是更灵活,所以如果你能用它来证明它,你应该只转换一个模板参数性能大幅提升.另一方面,如果您发现自己在一组枚举中编写了大量重复的代码,那么您应该尝试使用模板进行分析.