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

1. 左值&右值

    int n = 10;
    int m = n;
    // 10 = n; // 字面量右值
    const char* s = "123abc";
    // "abcd" = s; // 字面量右值
    m = n + 2;
    // n + 2 = m; // 中间结果右值
    string str = string(s);
    // string(s) = str; // 匿名对象右值
  • 右值:只能在=右边使用的值(字面量、中间结果、临时对象/匿名对象),无法直接取地址,不能使用左值引用。
  • 左值:可以在=左边使用的值

2. 左值引用&右值引用

#include 
 
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& f = 10; // 字面量不能初始化左值引用

    int m = n;     
    Print(m); // int n = m;
    Print(10);// int n = 10;

    Print2(m); // int& n = m;
    // Print2(10);// int& n = 10; // 右值不能初始化左值引用
    
    Print3(m); // const int& n = m;
    Print3(10);// const int& n = 10; // const左值引用可以初始化左值和右值
 
    // Print4(m); // int&& n = m; // 左值不能初始化右值引用
    Print4(10);// int&& n = 10;
}
  1. 左值引用只能用左值初始化
  2. 右值引用只能用右值初始化
  3. const左值引用可以初始化左值和右值。

右值引用是左值还是右值?

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

3.1 不可复制对象的移动

#include 
 
using namespace std;
// 不可复制对象的移动
class Uncopy{
public:
    Uncopy() = default;
    ~Uncopy() = default;
    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 v;
    return move(v);
    // return Uncopy(); // 匿名对象
}
int main(){
    Uncopy u;
    // Uncopy w = u; // 拷贝构造
    Uncopy w = Uncopy(); // 移动构造
    Uncopy w2 = move(u); // 移动构造
    Uncopy v;
    // v = u; // 赋值运算符重载
    v = Uncopy(); // 移动运算符重载
    v = move(u); // 移动运算符重载
 
    // Param(u); // Uncopy w =u;
    Param(Uncopy()); // Uncopy w =u;
    Param(move(u)); // Uncopy w =u;
    Return(); 
}

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

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

3.2 可复制对象的移动

#include 
#include 
 
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(){
    Simple simple;
    Simple simple2 = move(simple);
    simple2 = move(simple); 
}

移动的条件:

  1. 只有右值才能移动,如果左值要移动需要转换成右值(move())。
  2. 对象的类实现了移动构造函数和移动赋值运算符重载 。否则,执行拷贝操作。

3.3 STL中的移动

#include 
#include 
 
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 vec1 = {1,2,3,4,5};
    vector vec2;
    vec2 = move(vec1);
    cout << vec1.size() << "," << vec2.size() << endl;
 
    vector vs;
    //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)); // 移动构造
}
  • C++11 string和容器都实现移动构造函数和移动赋值运算符重载。
  • C++11 STL容器提供了移动操作的成员函数emplace_*()

4. 万能引用&完美转发

#include 
#include 
 
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;}
 
    void Test()&{ // 相当于void Test(Simple*& this)
       cout << "lvalue Test" << endl;
    }
    void Test()&&{ // 相当于void Test(Simple*&& this)
       cout << "rvalue Test" << endl;
    }
};
 
 
// void Func(Simple s){}
void Func(Simple& s){
   cout << "lvalue" << endl;
}
void Func(Simple&& s){
   cout << "rvalue" << endl;
}
// void Func(const Simple& s){}
  
template
void TemplateFunc(T&& param){// 模板参数&&:通用引用/万能引用,根据参数左右值类型的不同,生成不同参数类型的函数。
    // Func(param);
    // 完美转发
    Func(forward(param));
    forward(param).Test();
}
 
int main(){
    Simple s;
    Func(s);
    Func(move(s));
 
    s.Test();
    Simple().Test();
 
    TemplateFunc(s);// 传入左值 T&& => T&  void TemplateFunc(Simple& param)
    TemplateFunc(Simple()); // 传入右值 T&& => T&& void TemplateFunc(Simple&& param)
}
  1. 函数参数中的&&表示右值引用,函数模板参数中的&&表示万能引用。通用引用/万能引用,根据参数左右值类型的不同,生成不同参数类型的函数。
  2. forward()完美转发根据万能引用参数的不同(左值引用还是右值引用),恢复参数的状态(左值引用保持左值,右值引用转成右值),实现函数调用的转发(传入左值,调用左值引用的函数;传入右值,调用右值引用的函数)。

你可能感兴趣的:(C++11左值右值、左值引用、右值引用、万能引用、完美转发)