十、C++11左值右值、左值引用、右值引用、万能引用、完美转发

10 C++11左值右值、左值引用、右值引用

10.1 左值&右值

左值:可以在=左边使用的值

右值:只能在=右边使用的值(字面量、中间结果、临时对象/匿名对象),无法直接取地址,不能使用左值引用。

10.2 左值引用&右值引用

实例:左值引用、右值引用

#include <iostream>
#include <string>
using namespace std;
void Print(int n){
    cout << n << endl;
}
void Print2(int& n){
    cout << n << endl;
}
void Print3(const int& n){
    cout << n << endl;
}
void Print4(int&& n){ //右值引用
    cout << n << endl;
}
int main(){
    //右值:只能在=右边使用的值(字面量\中间结果\临时对象或匿名对象),无法取地址,不能左值引用
    //左值:可以在=左边使用的值
    int n = 10;
    int m = n;
    //10 = n; //字面量不能左值引用
    //n+2 = m; //中间结果不能左值引用
    const char* s = "123abc";
    //"abcd" = s;
    string str = s;
    //string(s) = str; 临时对象不能左值引用
    
    Print(m);
    Print(10);

    Print2(m); //int& n = m;
    //Print2(10); //int& n = 10; 常量不能被引用
    //int& f = 10; //右值不能初始化左值引用

    Print3(m); //const int& n = m;
    Print3(10); //const int& n = 10; 补丁:const左值引用可以初始化左值和右值

    //Print4(m); int&& n = m; 左值不能初始化右值引用
    Print4(10);
}
[root@localhost 9]# ./a.out 
10
10
10
10
10
10

结论:
1、左值引用只能用左值初始化
2、右值引用只能用右值初始化
3、const左值引用可以初始化左值和右值

10.3 移动构造函数&移动赋值运算符重载

10.3.1 不可复制对象的移动

实例:

#include <iostream>
using namespace std;
class Uncopy{
public:
    Uncopy(){cout << "constructor" << endl;}
    ~Uncopy(){cout << "destructor" << endl;}
    Uncopy(const Uncopy&) = delete; //禁用赋值
    Uncopy& operator=(const Uncopy&) = delete; //禁用拷贝构造
    Uncopy(Uncopy&&){cout << "rvalue copy constructor" << endl;}
    Uncopy& operator=(Uncopy&&){cout << "rvalue assign" << endl; return *this;}
};
void Param(Uncopy w){}

Uncopy Return(){
    Uncopy u;
    return u;
}
// 移动的条件:只有右值可以移动
// 左值变右值:move()
int main(){
    Uncopy u;
    //Uncopy v = u; //拷贝构造
    Uncopy w = Uncopy();
    Uncopy v;
    v = move(u); //赋值运算符重载
    v = Uncopy(); //移动运算符重载

    //Param(Uncopy()); //Uncopy w = Uncopy();
    Param(move(u)); //Uncopy w = u; 
    Return();
}
constructor
constructor
rvalue copy constructor
destructor
constructor
rvalue assign
constructor
rvalue assign
destructor
rvalue copy constructor
destructor
constructor
rvalue copy constructor
destructor
destructor
destructor
destructor
destructor

不可复制对象,可以使用移动语法做拷贝构造和赋值。

  • 移动的条件:
    1、只有右值才能移动,如果左值要移动需要转换成右值(move())。
    2、对象的类要实现移动构造函数和移动赋值运算符重载。

10.3.2 可复制对象的移动

实例:

#include <vector>
using namespace std;
//可复制对象使用移动
class Simple{
public:
    Simple(){cout << "constructor" << endl;}
    Simple(const Simple&){cout << "copy constructor" << endl;}
    Simple& operator=(const Simple&){cout << "assign constructor" << endl; return *this;}
    Simple (Simple&&){cout << "move constructor" << endl;}
    Simple& operator=(Simple&&){cout << "move override" << endl; return *this;}
    ~Simple(){cout << "destructor" << endl;}
    //加const只能放左值,不加const既能放左值,也能放右值
};
int main(){
    string s = "abc";
    //string t = s;
    string t = move(s);
    cout << (void*)s.c_str() << ":" << s << endl; //s执行move之后内容为空
    cout << (void*)t.c_str() << ":" << t << endl;

    vector<int> vec = {1,2,3};
    vector<int> vec2;
    vec2 = move(vec);
    cout << vec.size() << " " << vec2.size() << endl; //vec被move之后内容为空,大小为0

    //移动条件下,如果类内未定义移动函数,使用拷贝函数,定义了则使用移动函数
    Simple s1;
    Simple s2 = move(s1); //移动构造
    s2 = move(s1); //移动重载
    
    //STL中的移动
    vector<Simple> vs;
    //vs.push_back(s1); //拷贝构造
    vs.emplace_back(move(s1)); //移动构造
}

0x7fff2bf29880:
0x7fff2bf29860:abc
0 3
constructor
move constructor
move override
move constructor
destructor
destructor
destructor
  • 移动的条件:
    1、只有右值才能移动,如果左值要移动需要转换成右值(move())。
    2、对象的类实现了移动构造函数和移动赋值运算符重载 。否则,执行拷贝操作。

10.3.3 STL中的移动

实例:

#include <iostream>
#include <vector>
 
using namespace std;
// 可复制对象使用移动
class Simple{
public:
    Simple(){cout<< "constructor" << endl;}
    Simple(const Simple&){cout<< "copy constructor" << endl;}
    Simple& operator=(const Simple&){cout<< "assign override" << endl; return *this;}
    Simple(Simple&&){cout<< "move constructor" << endl;}
    Simple& operator=(Simple&&){cout<< "move override" << endl; return *this;}
};
int main(){
    string s = "abcd";
    // string t = s;
    string t = move(s);
    cout << (void*)s.c_str() << " " << s << endl;
    cout << (void*)t.c_str() << " " << t << endl;
 
    vector<int> vec1 = {1,2,3,4,5};
    vector<int> vec2;
    vec2 = move(vec1);
    cout << vec1.size() << "," << vec2.size() << endl;
 
    vector<Simple> vs;
    Simple simple;
    //vs.push_back(simple); // 拷贝构造
    //vs.push_back(simple); // 拷贝构造
    //vs.push_back(simple); // 拷贝构造
    //vs.push_back(simple); // 拷贝构造
    vs.emplace_back(move(simple)); // 移动构造
    vs.emplace_back(move(simple)); // 移动构造
    vs.emplace_back(move(simple)); // 移动构造
}
0x7ffc13aebf70 
0x7ffc13aebf50 abcd
0,5
constructor
move constructor
move constructor
copy constructor
move constructor
copy constructor
copy constructor

C++11 string和容器都实现移动构造函数和移动赋值运算符重载。
C++11 STL容器提供了移动操作的成员函数emplace_*()

结果分析:
这里vsmove第一个simple对象时候申请1个空间并放入vs[0]move第二个simple时候,先申请2个空间,move第二个simple 放入vs[1],再把第一个simple拷贝到vs[0]的位置。同理,move第三个simple时候,申请4个空间,move第三个simplevs[2],然后拷贝前两个simple分别放入vs[0]vs[1]中。

注:vector每次申请1、2、4、8…个空间

10.4 万能引用&完美转发

实例:

#include <iostream>
#include <vector>
using namespace std;
//可复制对象使用移动
class Simple{
public:
    Simple(){cout << "constructor" << endl;}
    Simple(const Simple&){cout << "copy constructor" << endl;}
    Simple& operator=(const Simple&){cout << "assign override" << endl; return *this;}
    Simple (Simple&&){cout << "move constructor" << endl;}
    Simple& operator=(Simple&&){cout << "move override" << endl; return *this;}
    
    //左值调用左值Test
    void Test()&{ //void Test(Simple*& this)
    	cout << "lvalue Test" << endl;
    }
    //右值调用右值Test
    void Test()&&{ //void Test(Simple*&& this)
    	cout << "rvalue Test" << endl;
    }
};
void Func(Simple& s){cout << "lvalue" << endl;}
void Func(Simple&& s){cout << "rvalue" << endl;}

template<typename T>
//模板参数&&:万能引用/通用引用,根据传入参数左右值类型不同,调用不同参数类型的函数
void TemplateFunc(T&& param){
    //Func(param);
    //完美转发
    Func(forward<T>(param));
    forward<T>(param).Test();
}
int main(){
    Simple s;
    Func(s); //左值
    Func(move(s)); //右值
    
    s.Test(); //调用左值Test
    Simple().Test(); //调用右值Test

    TemplateFunc(s); //传入左值 T&& => T& void TemplateFunc (Simple& param)
    TemplateFunc(Simple()); //传入右值 T&& ==> T&& void TemplateFunc(Simple&& param)
}
[root@localhost 9]# ./a.out 
constructor
lvalue
rvalue
lvalue Test
constructor
rvalue Test
lvalue
lvalue Test
constructor
rvalue
rvalue Test

1、函数参数中的&&表示右值引用,函数模板参数中的&&表示万能引用。通用引用/万能引用,根据参数左右值类型的不同,生成不同参数类型的函数
2、完美转发 forward<T>()根据万能引用参数的不同(左值引用还是右值引用),恢复参数的状态(左值引用保持左值,右值引用转成右值),实现函数调用的转发(传入左值,调用左值引用的函数;传入右值,调用右值引用的函数)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值