最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++深入右值引用之移動(dòng)語(yǔ)義與完美轉(zhuǎn)發(fā)(最新推薦)

 更新時(shí)間:2026年05月12日 09:27:26   作者:無(wú)限進(jìn)步_  
本文給大家介紹了C++深入右值引用之移動(dòng)語(yǔ)義與完美轉(zhuǎn)發(fā),文章討論了移動(dòng)構(gòu)造與移動(dòng)賦值的概念,以及它們?cè)谌萜髦械膽?yīng)用,感興趣的朋友一起看看吧

1. 左值與右值

左值和右值不是新概念,C++98就有,但C++11賦予了它們更重要的地位。

  • 左值:可以取地址的表達(dá)式,有持久狀態(tài)。變量、解引用的指針、數(shù)組元素都是左值。
  • 右值:不能取地址。字面量、臨時(shí)對(duì)象、表達(dá)式求值的中間結(jié)果。
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;            // *p 是左值
string s("hello");
s[0] = 'x';         // s[0] 是左值
10;                 // 右值
x + y;              // 右值
string("hello");    // 右值
// cout << &10;     // 錯(cuò)誤:右值不能取地址

C++11進(jìn)一步細(xì)化了:右值分為純右值(字面量、臨時(shí)對(duì)象等)和將亡值move返回的右值引用)。泛左值包含左值和將亡值。這些概念不必死記,關(guān)鍵是記住核心區(qū)別:能否取地址

2. 左值引用與右值引用

左值引用(T&)給左值取別名,右值引用(T&&)給右值取別名。

int& r1 = b;          // 左值引用綁定左值
int&& rr1 = 10;       // 右值引用綁定右值
double&& rr2 = x + y;
string&& rr3 = string("hello");

幾個(gè)規(guī)則:

  • 左值引用不能直接綁定右值,但const左值引用可以。
  • 右值引用不能直接綁定左值,但可以通過(guò)std::move轉(zhuǎn)換。
const int& cr = 10;           // OK
int&& rr4 = move(b);          // OK,move后的b仍可使用但資源已被取走

move()本質(zhì)上是一個(gè)強(qiáng)制類(lèi)型轉(zhuǎn)換:static_cast<remove_reference_t<T>&&>(arg)。它本身不移動(dòng)任何東西,只是給了你一個(gè)右值引用,真正的動(dòng)作由移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值完成。

有一個(gè)容易困惑的點(diǎn):右值引用變量本身的表達(dá)式屬性是左值。因?yàn)樗忻帧⒖梢匀〉刂贰?/p>

int&& rr = 10;
cout << &rr << endl;   // 可以取地址,rr是左值
int& lr = rr;          // OK,rr作為左值可以綁定左值引用
// int&& rr2 = rr;     // 錯(cuò)誤,rr是左值,不能直接綁定右值引用
int&& rr2 = move(rr);  // OK

這個(gè)設(shè)計(jì)看似別扭,但恰恰是移動(dòng)語(yǔ)義能正常工作的基礎(chǔ)——后面會(huì)看到。

3. 移動(dòng)構(gòu)造與移動(dòng)賦值

左值引用已經(jīng)在函數(shù)傳參和返回值中減少了大部分拷貝,但有一種場(chǎng)景它搞不定:返回局部對(duì)象。

string addStrings(string num1, string num2) {
    string str;
    // ... 構(gòu)建str ...
    return str;  // str是局部對(duì)象,函數(shù)結(jié)束就銷(xiāo)毀,不能傳引用返回
}

C++98被迫拷貝,C++11通過(guò)移動(dòng)語(yǔ)義解決了這個(gè)問(wèn)題:既然str馬上就要銷(xiāo)毀,把它的資源“偷”過(guò)來(lái)就行,沒(méi)必要深拷貝。

移動(dòng)構(gòu)造和移動(dòng)賦值接受一個(gè)右值引用參數(shù),核心操作是交換資源,而不是復(fù)制。

namespace demo {
class string {
public:
    string(const char* str = "")
        : _size(strlen(str)), _capacity(_size)
    {
        _str = new char[_capacity + 1];
        strcpy(_str, str);
    }
    // 拷貝構(gòu)造
    string(const string& s) {
        // 深拷貝...
    }
    // 移動(dòng)構(gòu)造
    string(string&& s) {
        swap(s);   // 直接把s的資源換到自己身上
    }
    // 拷貝賦值
    string& operator=(const string& s) {
        // 深拷貝...
    }
    // 移動(dòng)賦值
    string& operator=(string&& s) {
        swap(s);
        return *this;
    }
    void swap(string& s) {
        std::swap(_str, s._str);
        std::swap(_size, s._size);
        std::swap(_capacity, s._capacity);
    }
    ~string() { delete[] _str; }
private:
    char* _str = nullptr;
    size_t _size = 0;
    size_t _capacity = 0;
};
}

使用效果:

demo::string s1("hello");
demo::string s2 = s1;              // 拷貝構(gòu)造
demo::string s3 = demo::string("world");  // 移動(dòng)構(gòu)造(臨時(shí)對(duì)象)
demo::string s4 = move(s1);        // 移動(dòng)構(gòu)造(顯式move)

編譯器還會(huì)進(jìn)一步優(yōu)化。在VS2022的release模式下,demo::string s3 = demo::string("world");可能直接被優(yōu)化成一次原地構(gòu)造,移動(dòng)構(gòu)造都不會(huì)調(diào)用——這就是返回值優(yōu)化(RVO)。

返回值場(chǎng)景中,如果提供了移動(dòng)構(gòu)造,沒(méi)有優(yōu)化時(shí)編譯器會(huì)優(yōu)先選擇移動(dòng)而不是拷貝,顯著提高效率。

4. 移動(dòng)語(yǔ)義在容器中的應(yīng)用

C++11之后,STL容器的push_back、insert都增加了右值引用版本:

void push_back(const T& x);   // 左值版本,內(nèi)部拷貝
void push_back(T&& x);        // 右值版本,內(nèi)部移動(dòng)

當(dāng)傳入左值,走拷貝;傳入右值,走移動(dòng)。

list<demo::string> lt;
demo::string s1("hello");
lt.push_back(s1);                          // 拷貝
lt.push_back(move(s1));                    // 移動(dòng)
lt.push_back("world");                     // 移動(dòng)(臨時(shí)string)
lt.push_back(demo::string("world"));       // 移動(dòng)

自定義容器也可以實(shí)現(xiàn)相應(yīng)的重載,內(nèi)部在節(jié)點(diǎn)構(gòu)造時(shí)用move把參數(shù)轉(zhuǎn)成右值。

5. 完美轉(zhuǎn)發(fā)與引用折疊

考慮一個(gè)模板函數(shù),想要把參數(shù)原樣轉(zhuǎn)發(fā)給另一個(gè)函數(shù):

template<class T>
void relay(T&& t) {
    func(t);  // t是左值,永遠(yuǎn)調(diào)用func的左值版本,即使傳進(jìn)來(lái)的是右值
}

問(wèn)題在于:t作為右值引用變量,表達(dá)式屬性是左值,所以func(t)調(diào)不到右值重載版本。要保留參數(shù)的原始值類(lèi)別,需要完美轉(zhuǎn)發(fā)

template<class T>
void relay(T&& t) {
    func(forward<T>(t));
}

forward的實(shí)現(xiàn)依賴(lài)于引用折疊規(guī)則。C++不允許直接定義引用的引用(int& &),但在模板和類(lèi)型推導(dǎo)中可以出現(xiàn)。規(guī)則只有一條:右值引用的右值引用折疊成右值引用,其余全部折疊成左值引用。

typedef int&  lref;
typedef int&& rref;
lref&  r1 = n;  // int&  &  → int&
lref&& r2 = n;  // int&  && → int&
rref&  r3 = n;  // int&& &  → int&
rref&& r4 = 1;  // int&& && → int&&

relay中,T的推導(dǎo)結(jié)果配合引用折疊,實(shí)現(xiàn)了“左值傳左值,右值傳右值”:

  • 實(shí)參是左值 int a → T推導(dǎo)為int& → T&&折疊為int&
  • 實(shí)參是右值 10 → T推導(dǎo)為int → T&&int&&

forward內(nèi)部也是通過(guò)static_cast<T&&>實(shí)現(xiàn)的,配合引用折疊,左值情況返回左值引用,右值情況返回右值引用。這樣就把參數(shù)的原始屬性一路傳下去了。

6. 默認(rèn)移動(dòng)構(gòu)造與移動(dòng)賦值

編譯器在特定條件下會(huì)自動(dòng)生成移動(dòng)構(gòu)造和移動(dòng)賦值:

  • 如果用戶(hù)沒(méi)有定義拷貝構(gòu)造、拷貝賦值、析構(gòu)函數(shù)中的任何一個(gè),編譯器會(huì)嘗試自動(dòng)生成移動(dòng)構(gòu)造和移動(dòng)賦值。
  • 對(duì)于內(nèi)置類(lèi)型成員,逐字節(jié)拷貝;對(duì)于自定義類(lèi)型成員,如果能移動(dòng)就移動(dòng),否則拷貝。

一旦你提供了移動(dòng)構(gòu)造或移動(dòng)賦值,編譯器就不會(huì)自動(dòng)生成拷貝構(gòu)造和拷貝賦值。所以如果你想兩者都有,需要自己顯式聲明,或者用= default

class Person {
public:
    Person(const Person&) = default;   // 顯式要拷貝
    Person(Person&&) = default;        // 顯式要移動(dòng)
};

也可以用= delete禁止某個(gè)函數(shù)。

移動(dòng)語(yǔ)義是C++11最核心的特性之一。它讓“快”從“避免拷貝”的編碼技巧,變成了語(yǔ)言級(jí)別的優(yōu)化路徑。

到此這篇關(guān)于C++深入右值引用之移動(dòng)語(yǔ)義與完美轉(zhuǎn)發(fā)(最新推薦)的文章就介紹到這了,更多相關(guān)C++右值引用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語(yǔ)言數(shù)組棧實(shí)現(xiàn)模板

    C語(yǔ)言數(shù)組棧實(shí)現(xiàn)模板

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言數(shù)組棧實(shí)現(xiàn)模板,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Qt實(shí)現(xiàn)簡(jiǎn)易QQ聊天界面

    Qt實(shí)現(xiàn)簡(jiǎn)易QQ聊天界面

    這篇文章主要為大家詳細(xì)介紹了Qt實(shí)現(xiàn)簡(jiǎn)易QQ聊天界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 淺析C語(yǔ)言中strtol()函數(shù)與strtoul()函數(shù)的用法

    淺析C語(yǔ)言中strtol()函數(shù)與strtoul()函數(shù)的用法

    這篇文章主要介紹了淺析C語(yǔ)言中strtol()函數(shù)與strtoul()函數(shù)的用法,注意其將字符串轉(zhuǎn)換成long型的區(qū)別,需要的朋友可以參考下
    2015-08-08
  • C語(yǔ)言數(shù)組超詳細(xì)講解中篇三子棋

    C語(yǔ)言數(shù)組超詳細(xì)講解中篇三子棋

    數(shù)組是一組有序的數(shù)據(jù)的集合,本篇將帶你結(jié)合數(shù)組來(lái)實(shí)現(xiàn)三子棋小游戲,上手實(shí)練更快的能夠掌握數(shù)組使用,感興趣的朋友來(lái)看看吧
    2022-04-04
  • C++中臨時(shí)對(duì)象的常見(jiàn)產(chǎn)生情況及其解決的方案

    C++中臨時(shí)對(duì)象的常見(jiàn)產(chǎn)生情況及其解決的方案

    這篇文章主要是探討常見(jiàn)的臨時(shí)對(duì)象產(chǎn)生的情況,及其如何避免和解決這種臨時(shí)對(duì)象產(chǎn)生的方式。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • VCPKG安裝和使用教程(經(jīng)驗(yàn)總結(jié))

    VCPKG安裝和使用教程(經(jīng)驗(yàn)總結(jié))

    這篇文章主要介紹了VCPKG安裝和使用教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-01-01
  • 帶你粗略了解C++中的深淺拷貝

    帶你粗略了解C++中的深淺拷貝

    這篇文章主要給大家介紹了關(guān)于c++中深淺拷貝以及寫(xiě)時(shí)拷貝實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • C語(yǔ)言中strlen() strcpy() strcat() strcmp()函數(shù)的實(shí)現(xiàn)方法

    C語(yǔ)言中strlen() strcpy() strcat() strcmp()函數(shù)的實(shí)現(xiàn)方法

    這篇文章主要介紹了C語(yǔ)言中strlen() strcpy() strcat() strcmp()函數(shù)的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2017-08-08
  • C++實(shí)現(xiàn)簡(jiǎn)易貪吃蛇游戲

    C++實(shí)現(xiàn)簡(jiǎn)易貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)易貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • vs2019創(chuàng)建dll以及使用的圖文教程

    vs2019創(chuàng)建dll以及使用的圖文教程

    本文主要介紹了vs2019創(chuàng)建dll以及使用的圖文教程,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論

徐汇区| 酒泉市| 康马县| 娱乐| 安丘市| 庄河市| 卫辉市| 保亭| 罗源县| 河东区| 蒙阴县| 合川市| 凭祥市| 绵阳市| 监利县| 思茅市| 北流市| 仲巴县| 紫金县| 蒙阴县| 浦江县| 勃利县| 和平区| 施秉县| 西华县| 乌拉特后旗| 固镇县| 万年县| 彭泽县| 万荣县| 个旧市| 江西省| 叶城县| 社旗县| 拜城县| 玉溪市| 莱芜市| 汉中市| 霍山县| 石渠县| 长子县|