使用非类型模板参数有什么好处?

时间:2014-06-18 03:12:57

标签: c++ templates

众所周知,在C ++中我们可以使用非类型模板参数,如int:

template <class T, int size>
void f(){...}

我想知道它与将参数传递给函数的普通方式有何不同:

template <class T>
void f(int size) {...}

我认为一个区别是模板size在编译时被评估,并在实例化模板时被替换为文字。因此,我怀疑(纠正我,如果我错了)每个不同的size值导致创建新的二进制代码(&#34; .text&#34;),这似乎是一个开销

任何人都可以告诉我这是否必要且值得吗?

4 个答案:

答案 0 :(得分:3)

  

因此,我怀疑(纠正我,如果我错了),每个不同大小的值都会导致创建新的二进制代码(&#34; .text&#34;),这似乎是一种开销。

实际上就是这种情况,这是代码膨胀的常见原因。您需要弄清楚何时需要为每个N生成不同的函数,以及何时需要编译器信息较少的单个函数(请注意,这不仅仅是为了提高性能,也是为了正确性)。

由于Matt已经提供了一个简单的例子,让我们处理一个通过引用获取数组的函数。:

template<typename T, size_t N>
size_t operateOnArray( T (&array)[N] )
{
     // Some complex logic, which could include:
    for (std::size_t i = 0; i < N; ++i) {
       // complicated stuff
    }
}

参数的类型是对数组的引用,编译器将验证数组是否真正具有N元素(并且它将推断出数组中值的类型)。与类似的C风格代码相比,这在类型安全性方面有了很大的改进:

size_t operateOnArray( T *array, size_t N)
{
     // Some complex logic, which could include:
    for (std::size_t i = 0; i < N; ++i) {
       // complicated stuff
    }
}

特别是,用户可能错误地传递了错误的值:

int array[10];
operateOnArray(arrah, 20); // typo!!!

在第一种情况下,编译器将推断出大小,并保证它是正确的。

当你提到这可能会增加代码大小并且它可以添加很多东西时,你会在头脑中敲击。想象一下,该函数足够复杂,无法内联,并想象在程序中最终调用的函数的大小从1到100.程序代码包含100个实例基本上相同的代码,唯一的区别是大小。

有解决方案,比如混合两种方法:

size_t operateOnArray( T *array, size_t N); // Possibly private, different name...
template<typename T, size_t N>
size_t operateOnArray( T (&array)[N] ) {
   operateOnArray(array, N);
}

在这种情况下,编译器将在C风格的函数中有一个复杂代码的单个副本,并将生成100个版本的模板,但这些版本很简单,足够简单,编译器将< / em>内联代码并将程序转换为等效的C风格方法,保证类型安全。

  

任何人都可以告诉我这是否必要且值得吗?

当模板内的代码需要将值作为编译时常量时,必要,例如在上面的代码中,您不能拥有一个函数参数,该参数是对{的数组的引用{1}} N仅在运行时可用的元素。在其他情况下,如N,需要静态创建适当大小的数组。无论如何,所有示例都共享:在编译时需要知道值。

值得,当它为您的程序添加类型安全性时(参见上面的示例),或者它是否允许更强的优化(函数指针/成员函数指针为非函数) -type参数可以内联函数调用。)

你应该知道一切都是有代价的,在这种情况下是二进制大小。如果模板足够小以至于代码可能内联,请不要担心,但如果代码非常复杂,请考虑使用混合方法,在需要时使用模板参数或者如果它提供了很大的优势,否则就是常规论点。

答案 1 :(得分:0)

f()想要分配该大小的C样式数组时(例如,f(){int array[size]; }),必须将大小作为非类型模板参数传递。如果将size作为函数参数传递,则程序将无法编译,因为在编译时不知道该大小。

答案 2 :(得分:0)

除了提供更大的优化能力之外,参数还可以参与模板推导(与函数参数不同),例如,这是查找命名数组中有多少项的常见方法:

template<typename T, size_t N>
size_t lengthof( T (&array)[N] )
{
     return N;
}

用法:

#include <iostream>

int main()
{
    wchar_t foo[] = L"The quick brown fox";
    std::wcout << "\"" << foo << "\" has " << lengthof(foo) - 1 << " characters.\n";
}

编译器可能会在编译时计算长度并将其直接替换为wcout << .......行,甚至不会调用运行时函数。

答案 3 :(得分:0)

Compile-time dimensional analysis。这个帮助我在实施物理模拟时尽早发现错误。