万能引用:T&&或auto&&
需要注意的是不能在已经确定T类型的情况下,如"func
引用叠加推导规则(引用折叠):
| 类模板型 | T实际类型 | 最终类型 |
|---|---|---|
| T& | R | R& |
| T& | R& | R& |
| T& | R&& | R& |
| T&& | R | R&& |
| T&& | R& | R& |
| T&& | R&& | R&& |
从上面可看出只要左值参与进来,最后推导的结果就是一个左值引用:
x12
3template<typename T>4void show_type(T t){5 std::cout<<typeid(t).name()<<std::endl;6}7template<typename T>8void normal_forwarding(T t){9 show_type(t);10}11std::string get_string(){12 return "hi";13}14int main() {15 std::string s = "hello world!";16 normal_forwarding(s);17}为了避免不必要的复制,我们可以使用引用作为参数:
xxxxxxxxxx1812
3template<typename T>4void show_type(T &t){5 std::cout<<typeid(t).name()<<std::endl;6}7template<typename T>8void normal_forwarding(T &t){9 show_type(t);10}11std::string get_string(){12 return "hi";13}14int main() {15 std::string s = "hello world!";16 normal_forwarding(s);17 normal_forwarding(get_string());18}这时候我的get_string()返回的是一个右值,如果想让normal_forwarding()既能接受左值又能接受右值在以前是这样干的:
xxxxxxxxxx41template<typename T>2void normal_forwarding(const T &t){3 show_type(t);4}但是这么做就不能更改传入t的值了,因为它前面加了const修饰,在了解万能引用后可以这样干:
xxxxxxxxxx41template<typename T>2void normal_forwarding(T &&t){3 show_type(t);4}由于这里会根据传入参数(左值或右值)不同给show_type,它的参数类型可以自动在传入参数的时候推导出来,用T表示即可,因此可改为:
xxxxxxxxxx1812
3template<typename T>4void show_type(T t){5 std::cout<<typeid(t).name()<<std::endl;6}7template<typename T>8void normal_forwarding(T &&t){9 show_type(t);10}11std::string get_string(){12 return "hi";13}14int main() {15 std::string s = "hello world!";16 normal_forwarding(s);17 normal_forwarding(get_string());18}在此基础上main()中的这一行:
xxxxxxxxxx11normal_forwarding(s);实际上传入的是一个左值,那如果想在传入左值让show_type()收到的参数也是左值,我们在normal_forwarding()中使用static_cast<T&&>将T转换为右值引用,这样无论t实际是什么类型的引用,根据引用折叠规则t总是能被解析成它本身的引用类型:
xxxxxxxxxx41template<typename T>2void normal_forwarding(T &&t){3 show_type(static_cast<T &&>(t));4}举个例子,在t是左值引用的前提下,根据上面引用折叠的表我们可以得到左值引用在引用折叠后得到的还是一个左值引用,同理,main()这一行:
xxxxxxxxxx11normal_forwarding(get_string());传入的是一个右值引用,右值引用字引用折叠后得到的还是一个右值引用
std库中提供了forward()函数,具体代码如下:
xxxxxxxxxx281/** 2 * @brief Forward an lvalue. 3 * @return The parameter cast to the specified type. 4 * 5 * This function is used to implement "perfect forwarding". 6 */ 7 template<typename _Tp> 8 _GLIBCXX_NODISCARD 9 constexpr _Tp&& 10 forward(typename std::remove_reference<_Tp>::type& __t) noexcept 11 { return static_cast<_Tp&&>(__t); } 12 13 /** 14 * @brief Forward an rvalue. 15 * @return The parameter cast to the specified type. 16 * 17 * This function is used to implement "perfect forwarding". 18 */ 19 template<typename _Tp> 20 _GLIBCXX_NODISCARD 21 constexpr _Tp&&22 //移除引用得到本身的值类型23 forward(typename std::remove_reference<_Tp>::type&& __t) noexcept 24 { 25 static_assert(!std::is_lvalue_reference<_Tp>::value, 26 "std::forward must not be used to convert an rvalue to an lvalue"); 27 return static_cast<_Tp&&>(__t); 28 } 其中我们可以用forward()去代替static_cast<T &&>,可以写成:
xxxxxxxxxx1812
3template<typename T>4void show_type(T t){5 std::cout<<typeid(t).name()<<std::endl;6}7template<typename T>8void perfect_forwarding(T &&t){9 show_type(std::forward<T>(t));10}11std::string get_string(){12 return "hi";13}14int main() {15 std::string s = "hello world!";16 perfect_forwarding(s);17 perfect_forwarding(get_string());18
这里涉及的remove_reference实现了上面的引用折叠,在打开type_traits.h文件可以看到以下定义:
xxxxxxxxxx181/// remove_reference23 template<typename _Tp>4 struct remove_reference5 { using type = __remove_reference(_Tp); };67 template<typename _Tp>8 struct remove_reference9 { using type = _Tp; };10
11 template<typename _Tp>12 struct remove_reference<_Tp&>13 { using type = _Tp; };14
15 template<typename _Tp>16 struct remove_reference<_Tp&&>17 { using type = _Tp; };18测试引用折叠&万能引用:
xxxxxxxxxx2312
3template<typename T>4void func(T&& arg) {5 if(std::is_same_v<T, int&> == 1){6 std::cout<<"左值引用"<<std::endl;7 }else if(std::is_same_v<T, int&> == 0){8 std::cout<<"右值引用"<<std::endl;9 }else{10 std::cout<<"非引用类型"<<std::endl;11 }12}13
14int main() {15 int value = 1;16 std::cout<<"测试传入非引用类型:"<<std::endl;17 func(1);18 std::cout<<"测试传入左值引用:"<<std::endl;19 func(value);20 std::cout<<"测试传入右值引用:"<<std::endl;21 func(std::move(value));22 return 0;23}完美转发的例子:
xxxxxxxxxx2412
3void print(int &x) {4 std::cout << "lValue: " << x << std::endl;5}6void print(int &&x) {7 std::cout << "rValue: " << x << std::endl;8}9void print(const char* str) {10 std::cout << "String: " << str << std::endl;11}12
13template<typename T>14void forward_print(T&& arg) {15 print(std::forward<T>(arg));16}17
18int main() {19 int value = 42;20 forward_print(value);21 forward_print(std::move(100));22 forward_print("Hello, world!");23 return 0;24}