可变参数模板中阻止右值引用的困惑:为何与std::cref表现不同?
为什么可变参数模板的delete重载在混合左值右值参数时不触发错误?
核心原因:重载决议中模板可行性的判断
先看std::cref的单参数情况
std::cref的两个重载:
template< class T > std::reference_wrapper<const T> cref( const T& t ) noexcept; template< class T > void cref( const T&& ) = delete;
当传递右值(比如cref(std::string("test")))时:
- 两个模板都能推导成功:第一个模板的
const T&可以绑定右值,第二个模板的const T&&也可以绑定右值。 - 重载决议会优先选择更匹配的版本:右值绑定到右值引用(
const T&&)的匹配度高于绑定到const左值引用(const T&),因此会选中被delete的重载,触发编译错误。
再看你的可变参数foo模板情况
你的两个重载:
template<typename ... Ts> void foo(const Ts& ... ts) { } template<typename ... Ts> void foo(const Ts&& ...) = delete;
关键在于当存在左值参数时,被delete的模板会直接变成不可行:
案例1:foo<std::string, std::string>(a, "sss")
这里显式指定了Ts为std::string, std::string:
- 第一个重载的参数是
const std::string&, const std::string&:- 第一个实参
a是左值,可以直接绑定到const std::string&; - 第二个实参
"sss"会隐式转换为std::string临时对象(右值),也能绑定到const std::string&,因此这个重载可行。
- 第一个实参
- 第二个重载的参数是
const std::string&&, const std::string&&:- 第一个实参
a是左值,而左值无法绑定到右值引用(哪怕是const的),因此这个重载对第一个参数不兼容,整个模板直接被排除在候选集之外。
- 第一个实参
- 最终只有第一个重载可行,编译通过。
案例2:foo<std::string, std::string>("aaaa", "sss")
两个实参都是字符串字面量,会转换为std::string右值:
- 两个重载都可行:第一个的
const std::string&可以绑定右值,第二个的const std::string&&也可以绑定右值。 - 重载决议选择匹配度更高的第二个重载(右值绑定到右值引用),触发delete错误。
总结
单参数场景下,右值可以同时匹配两个重载,因此delete版本会被选中;但可变参数场景中,只要有一个左值参数,delete版本就会因为参数不兼容被排除,只能选择第一个重载。这是C++重载决议中模板可行性优先于匹配度的规则导致的:如果一个模板在某个参数上无法绑定实参,就会直接被淘汰,不会进入后续的匹配度比较环节。
内容的提问来源于stack exchange,提问作者tejas
相关产品推荐
相关产品推荐

