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

移动的条件:

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

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;
    //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 <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;}
 
    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<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();
    Simple().Test();
 
    TemplateFunc(s);// 传入左值 T&& => T&  void TemplateFunc(Simple& param)
    TemplateFunc(Simple()); // 传入右值 T&& => T&& void TemplateFunc(Simple&& param)
}
  1. 函数参数中的&&表示右值引用,函数模板参数中的&&表示万能引用。通用引用/万能引用,根据参数左右值类型的不同,生成不同参数类型的函数。
  2. forward<T>()完美转发根据万能引用参数的不同(左值引用还是右值引用),恢复参数的状态(左值引用保持左值,右值引用转成右值),实现函数调用的转发(传入左值,调用左值引用的函数;传入右值,调用右值引用的函数)。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容