使用非类型模板参数有什么好处?
What's good of using non-type template parameters?
众所周知,在c++中可以有非类型模板形参,如int:
template <class T, int size>
void f(){...}
我想知道它与将参数传递给函数的普通方式有何不同:
template <class T>
void f(int size) {...}
我认为一个区别是,对于模板,size
是在编译时计算的,并在实例化模板时作为字面量替换。因此,我怀疑(如果我错了请纠正我)每个不同的size
值都会导致创建新的二进制代码("。text"),这似乎是一个开销。
谁知道这是必要的和值得的?
因此,我怀疑(如果我错了请纠正我)每个不同大小的值都会导致创建新的二进制代码("。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
}
}
参数的类型是对数组的引用,编译器将为您验证数组是否确实具有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个版本的模板,但这些都很简单,简单到编译器将内联代码并将程序转换为具有保证类型安全的c风格方法的等效程序。
谁知道这是必要的和值得的?
是必要的当模板内的代码需要该值作为编译时常数时,例如在上面的代码中,你不能有一个函数参数是对N
元素数组的引用,而N
只在运行时可用。在其他情况下,如std::array<T,N>
,需要静态地创建一个适当大小的数组。无论如何,所有的例子都表明:值需要在编译时已知。
如果为程序增加了类型安全性(参见上面的示例),或者如果它允许更强的优化(将函数指针/成员函数指针作为非类型参数的函子可以内联函数调用),那么是值得的。
你应该意识到一切都是有代价的,在这个例子中是二进制大小。如果模板足够小,代码很可能被内联,不用担心,但如果代码非常复杂,请考虑使用混合方法,在需要时使用模板参数,或者如果它提供了很大的优势,则使用常规参数。
除了提供更大的优化能力外,形参还可以参与模板演绎(与函数实参不同),例如,这是查找命名数组中有多少项的常见方法:
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 << .......
行,甚至不进行运行时函数调用。
传递size作为非类型模板参数是必要的,当f()
想要分配该大小的c风格数组时(例如,作为f(){int array[size]; }
)。如果将size作为函数参数传递,程序将无法编译,因为在编译时不知道该大小。
编译时量纲分析。
- 传递给std::function template的template参数究竟代表什么
- 什么..(省略号)作为函数原型中唯一的函数参数,C++?
- C++部分概念 id:显式模板规范顺序/第一个参数的特殊状态的原因是什么?
- 引用捕获和在 lambda 中通过引用发送参数有什么区别 (C++)
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- 读取大文件(>2GB)(文本文件包含以太网数据)并通过不同参数随机访问数据的最佳方法是什么?
- 未命名的非类型模板参数有什么意义?
- 有什么方法可以将具有不同模板参数的模板类实例放入向量中?
- 什么是仅调用一次并调用参数的控制台应用
- 在模板参数中使用 {} 在 type_trait{} 中时,其作用是什么<T>?
- 在 C 和 C++ 中作为函数参数,int **a 和 int a[][] 之间有什么确切的区别
- 为什么或在什么情况下,你会将参数作为C++中的引用(或指针)传递给函数?
- 没有大小参数的CString::GetBuffer()做什么
- 可变参数模板和省略号有什么区别?
- 当通知迭代器参数初始化为空列表的开头时,list::insert 行为是什么?
- C++ [错误] 声明'char '隐藏参数。什么意思?
- 用相同的参数声明两个构造函数的最偶像化的方法是什么?
- 这个公式在"accumulate"时采用什么参数?
- C++ 在发送什么参数方面遇到问题
- FindConnectionPoint,传递什么参数