使用非类型模板参数而不是常规参数的原因

Reason for using non-type template parameter instead of regular parameter?

本文关键字:参数 常规 类型      更新时间:2023-10-16

在c++中,您可以使用如下的非类型模板参数创建模板:

template< int I >
void add( int& value )
{
  value += I;
}
int main( int argc, char** argv )
{
  int i = 10;
  add< 5 >( i );
  std::cout << i << std::endl;
}

输出"15"到计数。这有什么用?是否有理由使用非类型模板形参,而不是像

这样更常规的形参:
void add( int& value, int amount )
{
  value += amount;
}

对不起,如果这已经问过了(我看了,但找不到任何东西)。

非类型模板参数有很多应用;以下是一些:

可以使用非类型参数来实现表示固定大小的数组或矩阵的泛型类型。例如,您可以在维度上参数化Matrix类型,因此您可以创建Matrix<4, 3>Matrix<2, 2>。如果正确地为这些类型定义了重载操作符,就可以防止因添加或相乘维度不正确的矩阵而导致的意外错误,并且可以使函数显式地传达它们接受的矩阵的预期维度。这可以通过在编译时检测违规来防止大量运行时错误的发生。

可以使用非类型参数通过模板元编程实现编译时函数求值。例如,下面是一个在编译时计算阶乘的简单模板:

template <unsigned n> struct Factorial {
    enum { 
       result = n * Factorial<n - 1>::result
    };
};
template <> struct Factorial<0> {
    enum {
       result = 1
    };
};

这允许您编写像Factorial<10>::result这样的代码来在编译时获得10!的值。这可以防止在运行时执行额外的代码。

此外,您可以使用非类型参数来实现编译时维度分析,这允许您定义千克、米、秒等类型,这样编译器就可以确保您不会意外地在表示米的地方使用千克等。

希望这对你有帮助!

在这种情况下,您可能是对的,但在某些情况下,您需要在编译时了解这些信息:

但是这个呢?

template <std::size_t N>
std::array<int, N> get_array() { ... }

std::array需要在编译时知道它的大小(因为它是在堆栈上分配的)。

你不能这样做:

std::array<int>(5);

这是编译时多态和运行时多态之间的典型选择。

从你的问题的措辞来看,你似乎在"普通"模板参数中没有看到任何异常,而认为非类型参数是奇怪的和/或多余的。实际上,同样的问题也可以应用于模板类型参数(您称之为"普通"参数)。相同的功能通常可以通过带有虚函数的多态类(运行时多态性)或通过模板类型参数(编译时多态性)来实现。也有人会问,为什么我们需要模板类型参数,因为几乎所有东西都可以使用多态类实现。在非类型参数的情况下,您可能希望有一天有这样的东西
template <int N> void foo(char (&array)[N]) {
  ...
}

在这种特殊情况下,实际上没有任何优势。但是使用这样的模板参数,你可以做很多你不能做的事情,比如有效地将变量绑定到函数(如boost::bind),在函数或类中指定编译时数组的大小(std::array就是一个现成的例子),等等。

例如,对于该函数,您可以编写如下函数

template<typename T>
void apply(T f) {
    f(somenum);
}

那么你可以给apply传递一个函数:

apply(&add<23>);

这是一个极其简单的例子,但它演示了原理。更高级的应用包括将函数应用于集合中的每个值,在编译时计算函数的阶乘等。

有很多原因,比如做模板元编程(检查Boost.MPL)。但是没有必要走那么远,c++ 11的std::tuple有一个访问器std::get<i>,需要在编译时索引,因为结果依赖于索引。

我能想到的值参数最常用的用法是std::get<N>,它检索std::tuple<Args...>的第n个元素。第二常用的是std::integral_constant及其主要衍生物std::true_typestd::false_type,它们在任何类型的性状类中都是普遍存在的。事实上,类型特征绝对充满了值模板参数。特别是,SFINAE技术利用签名<typename T, T>的模板来检查类成员的存在。