
模板函数怎么写才不报错
直接写 template<typename T> 然后跟函数声明,是最小可行写法。但实际出错往往不是语法不对,而是类型没被推导出来,或者推导结果不符合预期。
常见错误现象:error: no matching function for call to 'xxx',尤其在传入字面量、临时对象或 const 引用时。
- 参数必须参与类型推导——如果某个
T只出现在返回值里(比如template<typename T> T make_value()),编译器根本没法猜,必须显式指定make_value<int>() - 避免对参数做隐式转换后再推导:传
42给void f(const std::string&)模板,T会推成std::string,但字面量"hello"是const char[6],不匹配;改用std::string_view更安全 - 转发引用要写成
T&&(万能引用),且必须配合std::forward<T>(x);写成T&就只是左值引用,无法绑定右值
auto 和模板参数推导的区别在哪
auto 是“单层”推导,模板是“带约束的多层”推导。两者规则不同,混用容易翻车。
使用场景:你写 auto x = func(); 得到的是具体类型;而 template<typename T> void g(T x) 调用时,T 推导还受实参是否加 const、是否为引用影响。
立即学习“C++免费学习笔记(深入)”;
-
int i = 42;→auto a = i;推出int;但g(i)中T也推成int,而g(42)同样推成int(字面量被“降级”为纯右值) -
const int ci = 42;→auto b = ci;还是int(auto忽略顶层const);但g(ci)中T推成const int,因为模板推导保留顶层const - 数组传参是经典坑:
int arr[3]; g(arr);→T推成int[3],不是int*;而auto p = arr;推成int*(数组退化)
什么时候必须用 std::declval 或 decltype
当你需要在不执行代码的前提下,获取某个表达式的类型——比如写 enable_if 判断、SFINAE 或概念约束时,绕不开它。
性能影响:零开销,纯编译期计算;但滥用会让错误信息变天书。
-
decltype(expr)直接取表达式类型,注意括号影响:decltype(x)是变量声明类型,decltype((x))是左值引用类型 -
std::declval<T>()是个纯虚构函数,只用于让decltype能“假装调用”一个不存在的对象,比如decltype(std::declval<T>().size())判断是否有size()成员 - 现代 C++20 后优先用
requires表达式替代,更可读;但底层机制仍依赖decltype和declval
模板参数不能自动推导的典型场景
不是所有地方都能靠编译器猜——有些语法结构天然阻断推导,硬写就会卡住。
兼容性影响:C++17 开始支持类模板参数推导(CTAD),但函数模板没这个待遇,别指望编译器替你补全。
- 函数返回值类型:如
template<typename T> std::vector<T> make_vec() { ... },调用make_vec()必须写make_vec<int>(),否则T无处可推 - 默认模板参数不参与推导:即使写了
template<typename T = double> void f(T),f(42)仍推int,不会fallback到double - 非类型模板参数(NTTP)完全不推:
template<size_t N> void foo(std::array<int, N>),传std::array<int, 5>才能推出N=5;传std::vector就彻底失败
类型推导看着是编译器的事,其实每一步都依赖你写的参数形式、实参的值类别和 cv 限定符。最常被忽略的是:字面量、临时对象、const 左值之间的细微差别,会在推导瞬间放大成编译错误。









