C++ 模板的引用参数推导

以下是海贼班上的学习笔记,加上自己的一些心得。

下面的swap代码可以正确运行。

#include <iostream>
using namespace std;

namespace haizei {
template<typename T>
void swap(T &a, T &b) {
    T c;
    c = a; a = b; b = c;
    return ;
}
} // end of haizei

int main() {
    int n = 123, m = 456;
    haizei::swap(n, m);
    cout << n << " " << m << endl; //输出456, 123
    return 0;
}

但是如果我们想调用haizei::swap(789, n)来把789的值赋给n,编译器出错。因为789是右值,不能绑定在左值引用的行参。
如果我们写成下面的形式,还是不对。

#include <iostream>
using namespace std;

namespace haizei {
template<typename T>
void swap(T &&a, T &&b) {
    T c;
    c = a; a = b; b = c;
    return ;
}
} // end of haizei

int main() {
    int n = 123, m = 456;
    haizei::swap(n, m);
    cout << n << " " << m << endl;
    haizei::swap(789, n);
    return 0;
}

注意,一般函数的参数里面,如果有int &&,那么这个表示右值引用。但是在模板函数里面,如果有T&说明是强制左值引用,但如果是T&&,那么这只是告诉编译器这里是参数的引用,可能是左值引用,也可能是右值引用。
在haizei::swap(n, m)里面,n是int,是左值, 所以T &&被推导成左值引用。那么如果T表示int的话就不对了,因为T&&就会是int &&,那就是右值引用了。所以T只能被推导成int &,那么T&&就是int &&&,注意这里发生了引用折叠,int &&&被折叠成int &.
所以T c;被推导成int &c; 而c又没初始化,所以出错。
再看一下haizei::swap(789, n);这一行,n是int,是左值。所以T也是推导成int &。789是右值,所以T &&表示右值引用,T被推导成int。那么就矛盾了。

如果我们把swap()函数写成下面的形式,那么haizei::swap(n, m)这一行编译是OK了,但是haizei::swap(789, n); 这一行编译还是不行,因为上面提到的T推导的矛盾。
而且虽然haizei::swap(n, m)编译通过了,但是实际上没用。,因为T c = a; 实际上是int & c = a, 这里c和a是一个变量,没法完成交换功能。打印结果会是456, 456。

template<typename T>
void swap(T &&a, T &&b) {
    T c = a;
    a = b; b = c;
    return ;
}

解决办法1:  去掉相关类型的引用,remove_reference<T>。所以T就被推导成int,而不是int &了。下面的程序可以正确对haizei::swap(n,m)进行交换。但是haizei::swap(789, n)还是通不过编译。我个人是觉得编译器还没进到remove_reference这一步,光看到swap的两个参数T&&a, T&&b一个推导成int,一个推导成int& 就觉得不对劲了。

#include <iostream>
using namespace std;

namespace haizei {
template<typename T>
void swap(T &&a, T &&b) {
    typename remove_reference<T>::type c;
    c = a; a = b; b = c;
    return ;
}
} // end of haizei

int main() {
    int n = 123, m = 456;
    haizei::swap(n, m);
    cout << n << " " << m << endl;
//    haizei::swap(789, n);
    return 0;
}

注意上面的swap()也可以直接写成

template<typename T>
void swap(T &&a, T &&b) {
    auto c = a; a = b; b = c;
    return ;
}

这里用auto能解掉引用,更简便。

最后,haizei::swap(789, n); 这行怎么办呢?只能采用2个模板参数了,因为789是右值,n是左值,只有一个模板参数T,编译器怎么推导都不对。
 

#include <iostream>
using namespace std;

namespace haizei {
template<typename T, typename U>
void swap(T &&a, U &&b) {
//    typename remove_reference<T>::type c;
    auto c = a; a = b; b = c;
    return ;
}
} // end of haizei

int main() {
    int n = 123, m = 456;
    haizei::swap(n, m);
    cout << n << " " << m << endl;
    haizei::swap(789, n);
    cout << n << endl;
    return 0;
}

上面的代码可以正确运行。输出结果
456 123
789

把swap()函数改写成

template<typename T, typename U>
void swap(T &&a, U &&b) {
    typename remove_reference<T>::type c;
    c = a; a = b; b = c;
    return ;
}

效果也是一样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值