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

Neo*_*989 1 c++ templates

众所周知,在 C++ 中我们可以有像 int 这样的非类型模板参数:

template <class T, int size>
void f(){...}
Run Code Online (Sandbox Code Playgroud)

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

template <class T>
void f(int size) {...}
Run Code Online (Sandbox Code Playgroud)

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

谁能告诉我们什么时候这是必要且值得的?

Dav*_*eas 5

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

事实确实如此,这也是代码膨胀的常见根源。您需要弄清楚何时要为每个函数生成不同的函数,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
    }
}
Run Code Online (Sandbox Code Playgroud)

参数的类型是对数组的引用,编译器会为您验证数组是否确实具有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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

int array[10];
operateOnArray(arrah, 20); // typo!!!
Run Code Online (Sandbox Code Playgroud)

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

当您提到这可能会增加代码大小,并且可能会增加很多代码时,您一语中的。想象一下该函数足够复杂以至于无法内联,并想象在您的程序中您最终会调用从 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);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器将在 C 风格函数中拥有一份复杂代码的副本,并将生成 100 个版本的模板,但这些都很简单,简单到编译器将内代码并转换程序相当于具有保证类型安全的 C 风格方法。

谁能告诉我们什么时候这是必要且值得的?

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

好吧当它为您的程序添加类型安全性时(请参见上面的示例),或者如果它允许更强的优化(将函数指针/成员函数指针作为非类型参数的函子可以内联函数调用),那么这是值得的。

而且您应该意识到,一切都是有代价的,在本例中是二进制大小。如果模板足够小,代码可能会被内联,请不要担心,但如果代码非常复杂,请考虑使用混合方法,在需要时使用模板参数,或者如果它提供了很大的优势和常规参数否则。