可变参数模板类中万能引用被识别为右值引用的问题排查
问题:模板中的
&&为何没成为万能引用? 我尝试创建一个可接收左值、右值及右值引用作为参数的可变参数模板类,但发现T&&被当作右值引用,无法接收其他类型参数。原以为模板中的&&是万能引用,是否遗漏了什么?
#include <string> template <typename... T> struct MyClass { MyClass(T&&... values) { } }; int main() { std::string string_value; //MyClass my_class(string_value); // class template argument deduction failed //MyClass<std::string> my_class(string_value); // cannot bind rvalue reference of type MyClass<std::string> my_class(std::move(string_value)); // OK return 0; }
原因分析
万能引用(转发引用)的生效有两个核心条件:
- 必须是模板参数的未修饰右值引用;
- 模板参数必须是编译器自动推导出来的,而非显式指定。
你的代码问题出在:
- 显式指定类模板参数时:比如
MyClass<std::string>,此时T被固定为std::string,T&&就变成了std::string&&(纯右值引用),只能绑定右值,自然无法接收左值string_value。 - 类模板参数推导(CTAD)失败:编译器尝试推导
T...时,左值string_value的类型是std::string&,而构造函数参数是T&&——若要匹配左值,T需推导为std::string&,但编译器默认不会将类模板参数推导为引用类型,导致推导失败。
解决方案
方案1:让构造函数成为独立模板
将构造函数改为模板函数,此时构造函数的模板参数U...会被自动推导,U&&就成为万能引用,能匹配左值、右值和右值引用:
#include <string> #include <utility> template <typename... T> struct MyClass { // 构造函数自身是模板,U&&为万能引用 template <typename... U> MyClass(U&&... values) { // 若需要完美转发参数,使用std::forward<U>(values)... } }; int main() { std::string string_value; MyClass my_class(string_value); // 正常推导,CTAD生效 MyClass<std::string> my_class2(string_value); // 构造函数的U被推导为std::string&,匹配左值 MyClass<std::string> my_class3(std::move(string_value)); // 匹配右值 return 0; }
方案2:添加推导指引(若需类模板参数匹配参数类型)
如果希望类模板的T...对应参数的实际类型(去除引用、cv修饰),可以添加推导指引,让编译器按规则推导类模板参数:
#include <string> #include <utility> #include <type_traits> template <typename... T> struct MyClass { template <typename... U> MyClass(U&&... values) { } }; // 推导指引:让类模板参数T...为参数类型的衰减类型(去除引用、const等) template <typename... U> MyClass(U&&...) -> MyClass<std::decay_t<U>...>; int main() { std::string string_value; MyClass my_class(string_value); // T推导为std::string MyClass my_class2(std::move(string_value)); // T推导为std::string MyClass<std::string> my_class3(string_value); // 正常匹配 return 0; }
内容的提问来源于stack exchange,提问作者uni
相关产品推荐
相关产品推荐

