模板重载传参能否使用引用?详解C++模板悬垂引用成因
你在《C++ Templates The Complete Guide(第二版)》1.5章遇到的这个示例,核心问题是引用绑定到临时变量后的生命周期问题,下面我们一步步拆解::max(s1, s2, s3)的完整调用过程,解释临时变量的来源和悬垂引用的成因:
示例代码
#include <cstring> template < typename T> T const& max(T const& a, T const& b) { return b < a ? a : b; } char const* max(char const* a, char const* b) { return std::strcmp(b, a) < 0 ? a : b; } template < typename T> T const& max(T const& a, T const& b, T const& c) { return max(max(a, b), c); } int main() { auto m1 = ::max(7, 42, 68); // OK char const* s1 = "frederic"; char const* s2 = "anica"; char const* s3 = "lucas"; auto m2 = ::max(s1, s2, s3); // undefined behaviour }
调用::max(s1, s2, s3)的完整过程
匹配三参数模板版本
编译器推导三参数max模板的类型参数T为char const*,实际函数签名为:char const* const& max(char const* const& a, char const* const& b, char const* const& c)函数内部执行
return max(max(a, b), c);,先处理内层的max(a, b)。内层调用匹配非模板重载
a和b是char const*类型,编译器优先匹配专门针对char const*的非模板重载版本:char const* max(char const* a, char const* b)这个函数比较字符串后,返回的是值类型的
char const*——比如返回s1的地址,但返回的是该地址的拷贝,不是s1的引用。外层调用匹配模板版本
内层调用的返回值是一个临时的char const*值,将它和s3一起传给max时,编译器会实例化双参数模板(因为非模板版本无法匹配“引用绑定临时值”的场景),此时T仍为char const*,实例化后的函数签名是:char const* const& max(char const* const& a, char const* const& b)这个模板函数的第一个参数是绑定到内层返回的临时指针变量的引用,最终返回这个引用。
临时变量销毁导致悬垂引用
内层非模板函数返回的临时指针变量,生命周期仅到外层max(max(a,b), c)表达式结束为止。而三参数模板返回的正是指向这个临时变量的引用,回到main函数时,临时变量已被销毁,m2绑定的是无效的悬垂引用,因此产生未定义行为。
疑问解答
非模板函数确实返回s1的地址,但这个返回值是值拷贝——即一个独立的临时char const*变量,它的值等于s1的地址,但本身是临时对象。模板版本的max返回的是这个临时对象的引用,而非直接返回s1的引用,这就是问题的核心。
内容的提问来源于stack exchange,提问作者f1msch

