问题的历史:
模板在 C++ 中被引入,以有效实现通用算法和数据结构。最初需要一种根据输入参数自动推导类型的机制,以便于开发者使用模板。
问题:
推导机制并不总是显而易见:在引用、类型掩蔽、部分特化、引用模板参数和常量之间常常存在歧义。有时编译器无法推导出类型,或者推导出意外的结果。
解决方案:
编译器分析参数,并将其与模板参数匹配,考虑 cv 限定符、引用和指针的规则。当自动推导不可行时,缺点和限制要求显式指定类型。
代码示例:
template<typename T> void func(T arg) { /* ... */ } func(10); // T 被推导为 int func("abc"); // T 被推导为 const char* // T arg 和 T& arg 的区别: template<typename T> void printRef(T& arg); // 不接受临时对象!
关键特性:
如果模板函数接受 T& 并尝试传递临时对象,会发生什么?
编译器将无法推导出类型,因为临时对象不能通过非常量引用传递。将出现编译错误。
template<typename T> void foo(T& arg); foo(42); // 错误!
与数组的类型推导是如何工作的?
数组在按值传递时会“衰变”为指针,但如果模板接受引用,则保留数组的大小,这通常用于实现安全数组模板。
template<typename T, size_t N> void arraySize(T (&arr)[N]) { std::cout << N; } int x[10]; arraySize(x); // 输出 10
为什么不总是正确使用 auto 来存储模板函数调用的结果?
在推导类型时,auto 可能会“切片”const 或引用限定符,有时导致意外的复制或可变性错误。
auto x = funcReturningRef(); // x 将为值,而不是引用!
通用排序的模板函数接受 T&,用户尝试传递临时对象。结果代码无法编译,错误让开发者感到困惑。
优点:
缺点:
使用通用引用(T&&)通过 std::forward 实现的排序器,能够合理地处理 lvalue 和 rvalue,从而提高性能和灵活性。
优点:
缺点: