带类型推导的模板类构造函数中如何正确使用universal reference
核心原因:万能引用的生效前提
万能引用(转发引用)的本质是模板参数推导过程中的引用折叠,必须同时满足两个条件才能生效:
- 参数形式必须是
T&& - 这里的
T是当前正在被推导的函数模板参数
你代码中所有报错的场景,本质都是不满足上面的前提:
- 类A的
T&&不是万能引用:T是类的模板参数,实例化类的时候T就已经确定,构造函数的T&&是普通右值引用,只能绑定右值,左值传入自然报错 - 类B的参数是左值引用,只能绑定左值,右值传入会报错
- 类C的构造函数本身是模板,但C++17之前类模板的构造函数不会自动推导类的模板参数,写
C c1(get_l_value())时编译器不知道类C的模板参数T应该是什么,直接推导失败
正确实现方案
C++17及以上版本(支持类模板实参推导CTAD)
只需要给类C加一条推导指引即可正常使用:
template<class T> struct C { // 如果需要完美转发参数到内部可以用std::forward template<class T2> C(T2&& a) : m_val(std::forward<T2>(a)) {} private: T m_val; }; // 新增推导指引,告诉编译器构造参数和类模板参数的对应关系 template<class T2> C(T2&&) -> C<std::decay_t<T2>>;
修改后你示例中c1、c2的调用都可以正常编译,std::decay_t的作用是去掉参数的引用、const/volatile限定符,得到原始类型。
C++11/14版本(不支持CTAD)
可以通过工厂函数绕开类模板参数推导的限制:
template<class T> C<typename std::decay<T>::type> make_C(T&& val) { return C<typename std::decay<T>::type>(std::forward<T>(val)); } // 调用时不需要手动指定参数 auto c1 = make_C(get_l_value()); auto c2 = make_C(get_r_value());
可编译的完整示例代码
#include <vector> #include <type_traits> #include <utility> template<class T> struct C { template<class T2> C(T2&& a) {} }; template<class T2> C(T2&&) -> C<std::decay_t<T2>>; template<class T> void foo(T&& a) { } auto get_r_value() { return std::vector<int>(100); } std::vector<int>& get_l_value() { static std::vector<int> v(100); return v; } int main() { foo(get_r_value()); // ok foo(get_l_value()); // ok C c1(get_l_value()); // ok C c2(get_r_value()); // ok }
内容的提问来源于stack exchange,提问作者user3324131
相关产品推荐
相关产品推荐

