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

C++ 右值引用(rvalue references)與移動語義(move semantics)深度解析

 更新時間:2025年12月11日 17:02:30   作者:點云SLAM  
文章主要介紹了C++右值引用和移動語義的設(shè)計動機、基本概念、實現(xiàn)方式以及在實際編程中的應(yīng)用,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧

一、右值引用(rvalue references)與移動語義(move semantics)設(shè)計動機

1.1 為什么需要移動語義

傳統(tǒng) C++ 的對象拷貝(copy)在管理資源(堆內(nèi)存、文件句柄、套接字、大數(shù)組等)時代價高。以前的做法:

  • 提供拷貝構(gòu)造/賦值,或
  • 使用指針/引用共享資源(容易出錯)或
  • 使用 swap 優(yōu)化復(fù)制(代價仍然存在)

移動語義的目標:當(dāng)可以“竊取”一個臨時對象的內(nèi)部資源而不是逐元素復(fù)制時,允許編譯器選擇把資源從源對象“移動”到目標對象,使得構(gòu)造與賦值的成本從 O(n) 變?yōu)?O(1)。這是通過 右值引用(T&& 與專門的 移動構(gòu)造函數(shù) / 移動賦值運算符 實現(xiàn)的。

1.2 為什么需要右值引用

在 C++11 之前:

  • 返回大對象(如 std::vector<PointXYZ>)會發(fā)生昂貴的 深拷貝(deep copy)
  • 臨時對象往往不需要被復(fù)制(因為臨時對象馬上就會銷毀)。
  • 無法區(qū)分 “臨時對象的引用” 與 “普通左值引用”。

因此:

std::vector<int> v;
// …
auto x = v;  // 必須深拷貝

深拷貝非常昂貴,尤其對于 SLAM 中:

  • 大規(guī)模點云
  • 特征向量(FPFH、SHOT、Descriptors)
  • 圖優(yōu)化結(jié)構(gòu)(因子圖、Jacobian Blocks)

右值引用 + 移動語義 就是解決上述性能瓶頸的關(guān)鍵。

二、值類別(value categories)——理解左右值很關(guān)鍵

C++11 之后共有 5 種值類別:

類別描述示例
lvalue(左值)有名字,可取地址x、v[0]
xvalue(將亡值)即將被銷毀的對象,可以“偷資源”std::move(x)T&& 某些表達式
prvalue(純右值)臨時值,無名稱T()3func() 返回臨時
glvalue(泛左值)lvalue + xvalue能代表 “對象的定位”
rvalue(右值)xvalue + prvalue可移動但不能取地址

記住要點:

  • 左值有身份,不必有值
  • 右值有值,不必有身份

右值引用就是綁定 xvalue 與 prvalue 的一種引用。

關(guān)鍵:右值引用 T&& 能接受 xvalue/prvalue,但不接受 lvalue(除非用 std::move 或模板完美轉(zhuǎn)發(fā))。

三、右值引用與移動構(gòu)造/賦值(基本定義)

對類 T,推薦實現(xiàn)(rule of five):

struct T {
    // 構(gòu)造/析構(gòu)
    T();                     // default ctor
    T(const T&);             // copy ctor
    T(T&&) noexcept;         // move ctor
    T& operator=(const T&);  // copy assign
    T& operator=(T&&) noexcept; // move assign
    ~T();
};

示例 — 簡單資源類(動態(tài)數(shù)組)

#include <iostream>
#include <utility> // std::move
struct Buffer {
    size_t size_;
    double* data_;
    Buffer(size_t n=0) : size_(n), data_(n ? new double[n] : nullptr) {}
    ~Buffer() { delete[] data_; }
    // copy
    Buffer(const Buffer& o) : size_(o.size_) {
        if (size_) {
            data_ = new double[size_];
            std::copy(o.data_, o.data_ + size_, data_);
        } else data_ = nullptr;
        std::cout<<"copy ctor\n";
    }
    // move
    Buffer(Buffer&& o) noexcept : size_(o.size_), data_(o.data_) {
        o.size_ = 0;
        o.data_ = nullptr;
        std::cout<<"move ctor\n";
    }
    // copy assign
    Buffer& operator=(const Buffer& o){
        if(this==&o) return *this;
        delete[] data_;
        size_=o.size_;
        data_ = size_? new double[size_]: nullptr;
        std::copy(o.data_, o.data_ + size_, data_);
        std::cout<<"copy assign\n";
        return *this;
    }
    // move assign
    Buffer& operator=(Buffer&& o) noexcept {
        if(this==&o) return *this;
        delete[] data_;
        size_ = o.size_;
        data_ = o.data_;
        o.size_ = 0;
        o.data_ = nullptr;
        std::cout<<"move assign\n";
        return *this;
    }
};

要點

  • 移動構(gòu)造/賦值竊取資源指針并把源對象置于安全的析構(gòu)態(tài)(通常設(shè)置為 nullptr、0)。
  • noexcept 很重要 —— 它允許容器(比如 std::vector<T>)在擴容時使用移動而不是拷貝(容器會在異常安全性不保證時退回拷貝策略)。

四、std::move、std::forward與完美轉(zhuǎn)發(fā)

  • std::move(x)把 lvalue 強制轉(zhuǎn)換成 xvalue(右值),告訴編譯器“可以竊取 x 的資源”。但它本身不移動;只是類型轉(zhuǎn)換。
  • std::forward<T>(x):在模板中保留值類別(完美轉(zhuǎn)發(fā))。當(dāng)模板參數(shù) TU&& 的情況下,forward 會在 T 為 lvalue-reference 時轉(zhuǎn)成 lvalue,否則轉(zhuǎn)成 rvalue。

示例:

void take_by_value(Buffer b) { /*...*/ }
Buffer b(100);
take_by_value(b);          // copy
take_by_value(std::move(b)); // move (resource stolen)

注意:對已 std::move 的對象繼續(xù)使用可能導(dǎo)致未定義語義(安全但不可預(yù)測狀態(tài)),稱為 use-after-move。移動后的對象必須處于析構(gòu)與賦值安全狀態(tài),但其具體內(nèi)容不可依賴,除非類型指定了明確語義。

五、RVO / NRVO 與返回值優(yōu)化

在很多情況下函數(shù)返回臨時對象時會有拷貝或移動?,F(xiàn)代編譯器會做 (命名)返回值優(yōu)化(RVO / NRVO),避免額外拷貝/移動。C++17 更嚴格地把 prvalue 語義演進,使得通常不會觸發(fā)移動或拷貝(直接在調(diào)用者處構(gòu)造返回對象)。

示例:

Buffer make_buffer(size_t n){
    Buffer tmp(n);
    // ... fill ...
    return tmp; // RVO: tmp 在調(diào)用處直接構(gòu)造
}

即便沒有 RVO,若 Buffer 有移動構(gòu)造,也會用移動構(gòu)造移動臨時對象(開銷?。?。

六、移動語義對標準容器與算法的影響

  • 容器(std::vector、std::deque 等)會在重新分配時盡量移動元素(若 T 的移動構(gòu)造 noexcept,容器使用移動;否則可能回退到拷貝)。因此,務(wù)必對能移動的大對象提供 noexcept 的移動構(gòu)造/賦值。
  • std::move_iteratorstd::make_move_iterator 可以把算法變?yōu)?ldquo;移動模式”:例如 std::copy(std::make_move_iterator(first), std::make_move_iterator(last), dest) 會把元素移動到 dest。
  • std::move_if_noexcept:在條件下選擇移動(如果移動拋異常則拷貝)。容器擴容等場景常使用這一策略。

七、移動語義和異常安全,為什么noexcept很重要

  • 如果移動構(gòu)造有可能拋出異常,容器在重分配時可能無法保證強異常安全,會選擇回退到拷貝策略(更安全但更慢)。
  • 因此 移動構(gòu)造/賦值應(yīng)盡可能聲明 noexcept。若必須在移動中做可能拋異常的操作,可以將那些操作放在拷貝 / swap 路徑中,或保證內(nèi)部操作不會拋異常。

推薦模式(move via swap):

T& operator=(T&& other) noexcept {
    swap(*this, other);
    return *this;
}

但要確保 swap 本身 noexcept。

八、設(shè)計上的注意事項與常見陷阱

1) 移動后的對象狀態(tài)

移動后對象應(yīng)該處于有效但未指定內(nèi)容的狀態(tài),可安全析構(gòu)和賦值,但不能假設(shè)其值。文檔化被移動后對象的可用操作(建議僅能被賦值或析構(gòu))是一種好習(xí)慣。

2) 禁止移動操作(make class non-moveable)

如果類管理不可轉(zhuǎn)移資源(例如與 OS 綁定的唯一句柄,或禁止移動的語義),可以刪掉移動構(gòu)造:

T(T&&) = delete;
T& operator=(T&&) = delete;

3) 輕量類型也可移動?是否必要?

對小 POD(如 int, double)移動沒有意義;移動語義主要針對“外部資源”。但實現(xiàn)移動構(gòu)造不會有壞處,只是多寫一點代碼。

4) 當(dāng)實現(xiàn)了自定義拷貝/移動時記住 Rule of Five

如果用戶定義了任一種:析構(gòu)(dtor)、拷貝構(gòu)造、拷貝賦值、移動構(gòu)造、移動賦值,應(yīng)考慮同時實現(xiàn)或禁用另外兩者以避免編譯器生成不合適的默認函數(shù)。

5) 完美轉(zhuǎn)發(fā)與重載決議

對于函數(shù)模板,重載 f(const T&)f(T&&) 時注意:f(T&&) 對于左值不會匹配除非使用模板參數(shù)推導(dǎo)或 std::move。這常用于實現(xiàn) emplace_back 或工廠函數(shù)。

九、move-only 類型(典型示例:std::unique_ptr)

std::unique_ptr 是只可移動不可拷貝的典型例子。它利用移動語義保證資源唯一性。使用 unique_ptr 可以安全地把資源傳遞給函數(shù)或容器(容器會移動 unique_ptr 對象)。

std::unique_ptr<Foo> make_foo() {
    return std::make_unique<Foo>();
}
std::vector<std::unique_ptr<Foo>> v;
v.push_back(make_foo()); // move into vector

注意:std::vector<T> 可以存放 move-only 類型(C++11 起)。

十、模板與完美轉(zhuǎn)發(fā)的典型模式(emplace與工廠)

完美轉(zhuǎn)發(fā) 用于把參數(shù)原樣傳遞給構(gòu)造函數(shù)或函數(shù),避免不必要拷貝/移動:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique_impl(Args&&... args){
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

std::vector::emplace_back(args...) 使用完美轉(zhuǎn)發(fā)在目標存儲處直接構(gòu)造對象,避免臨時對象再移動/拷貝(相對于 push_back(T(args...)) 更高效)。

十一、性能分析

  1. 測量拷貝 VS 移動:在 Debug 模式下可能看不到差異(優(yōu)化關(guān)閉),要在 Release 編譯并測量真實應(yīng)用場景(例如大量點云向量移動)。
  2. profile container reallocation:如果類型的移動不是 noexceptstd::vector 可能在擴容時拷貝元素造成意外開銷。檢查移動構(gòu)造是否 noexcept。
  3. 避免不必要的 std::move:對返回局部變量的 return std::move(local) 會阻止 RVO,并且通常會產(chǎn)生多余的 move。不要在 return 語句中對返回值使用 std::move(C++11/14 中反模式,C++17 prvalue 語義進一步緩解)。

十二、實戰(zhàn)建議

1) 表達式模板 / 延遲求值庫(Eigen、std::string 復(fù)制優(yōu)化)

大型數(shù)學(xué)庫(Eigen)使用表達式模板避免臨時對象。移動語義配合表達式模板可以極大減少分配和復(fù)制。

2) 委托內(nèi)存池 + move

當(dāng)對象使用自定義內(nèi)存池(固定內(nèi)存區(qū)域)時,移動構(gòu)造往往只是指針/偏移值的復(fù)制,性能幾乎是常數(shù)。

3) 多線程注意

在多線程場景中移動對象時要注意競爭:移動操作不是線程安全的;在移動前應(yīng)保證沒有其他線程同時訪問/修改該對象。

4)std::function與 move-only 可調(diào)用對象

std::function 在 C++11 中要求可拷貝目標;若要傳遞 unique_ptr 到回調(diào),可使用 std::move 包裝 lambda 捕獲: auto cb = [p = std::move(ptr)](){ ... };。C++17/20 中有更多靈活性(std::move_only_function 提案/實現(xiàn))。

十三、實戰(zhàn)注意要點

  • 為管理資源的類實現(xiàn)移動構(gòu)造/移動賦值,且聲明 noexcept(如果安全)。
  • 遵守 Rule of Five:若實現(xiàn)自定義 dtor/copy/move/assign,考慮實現(xiàn)完整五條或禁用不需要的。
  • 對外部接口盡量接受 T&& 或模板轉(zhuǎn)發(fā)參數(shù)以便移動(如 push_back(T&&) / emplace_back)。
  • 在庫/容器中使用 std::move_if_noexcept 或合理選擇異常策略,以保證強異常安全。
  • 避免在 return 中使用 std::move(local)(破壞 RVO)。
  • 文檔化移動后對象的狀態(tài)與可調(diào)用操作(明確語義)。
  • 在調(diào)試/性能測量時使用 Release 編譯并測量實際熱點(容器 reallocation、頻繁返回臨時等)。
  • 在多線程中確保移動前后沒有并發(fā)訪問。
  • 使用 std::unique_ptr 作為首選的 movable-only 智能指針(比裸指針安全)。

十四、典型示例與對比

下面是一個短示例展示 std::vector<Buffer> 在 reallocation 時如何受 noexcept 影響:

#include <vector>
#include <iostream>
struct NoExceptBuffer {
    NoExceptBuffer(NoExceptBuffer&&) noexcept { }
    NoExceptBuffer& operator=(NoExceptBuffer&&) noexcept { return *this; }
    NoExceptBuffer() = default;
};
struct MayThrowBuffer {
    MayThrowBuffer(MayThrowBuffer&&) { } // NOT noexcept
    MayThrowBuffer& operator=(MayThrowBuffer&&) { return *this; }
    MayThrowBuffer() = default;
};
int main(){
    std::vector<NoExceptBuffer> v1;
    v1.reserve(100);
    for(int i=0;i<100;++i) v1.emplace_back();
    std::vector<MayThrowBuffer> v2;
    v2.reserve(100);
    for(int i=0;i<100;++i) v2.emplace_back();
    std::cout<<"Done\n";
}

在某些實現(xiàn)中,v1 在擴容時會將元素移動(更快),而 v2 因移動可能拋異常,會退回到拷貝(或觸發(fā)更復(fù)雜安全性處理),性能差異明顯。

十五、總結(jié)

  • 移動語義 允許“竊取”臨時對象資源,極大減少高成本拷貝,提升性能。
  • 右值引用 T&&std::move / std::forward 是實現(xiàn)移動語義與完美轉(zhuǎn)發(fā)的基礎(chǔ)工具。
  • 實現(xiàn)移動時:務(wù)必保證移動后對象處于析構(gòu)/賦值安全狀態(tài),并盡量聲明 noexcept。
  • 容器/算法 會基于可用的移動構(gòu)造采取不同策略(移動優(yōu)于拷貝),因此 move/noexcept 的設(shè)計會影響庫級別性能。
  • 實踐:用 unique_ptremplace、make_move_iterator、完美轉(zhuǎn)發(fā)等現(xiàn)代 C++ 工具編寫高性能、異常安全的代碼。

到此這篇關(guān)于C++ 右值引用(rvalue references)與移動語義(move semantics)深度詳解的文章就介紹到這了,更多相關(guān)C++ 右值引用與移動語義內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解C++模擬實現(xiàn)priority_queue(仿函數(shù))

    詳解C++模擬實現(xiàn)priority_queue(仿函數(shù))

    本文主要介紹了關(guān)于C++中優(yōu)先級隊列的模擬實現(xiàn),以及仿函數(shù)的使用,優(yōu)先級隊列是一種容器適配器,其第一個元素總是最大的,仿函數(shù)本質(zhì)是一個類,重載了operator(),可改變比較邏輯,同時,文中還介紹了如何進行類的比較,如日期類或其指針的比較,以及庫中sort函數(shù)的使用方法
    2024-10-10
  • C++類和對象之相關(guān)特性解讀

    C++類和對象之相關(guān)特性解讀

    這篇文章主要介紹了C++類和對象之相關(guān)特性,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • 利用Matlab復(fù)刻兩款粒子愛心動畫效果

    利用Matlab復(fù)刻兩款粒子愛心動畫效果

    最近電視劇《點燃我,溫暖你》大火,蹭一下熱度,發(fā)一下MATLAB畫愛心的代碼,寫的比較隨意,大家可以自行調(diào)整粒子大小和顏色啥的
    2022-11-11
  • C++廣播通信實例

    C++廣播通信實例

    這篇文章主要介紹了C++實現(xiàn)廣播通信的方法,實例講述了C++ socket廣播通信的原理與實現(xiàn)方法,需要的朋友可以參考下
    2014-10-10
  • C++中的拷貝構(gòu)造函數(shù)詳解

    C++中的拷貝構(gòu)造函數(shù)詳解

    大家好,本篇文章主要講的是C++中的拷貝構(gòu)造函數(shù)詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02
  • C++中new關(guān)鍵字用法示例詳解

    C++中new關(guān)鍵字用法示例詳解

    new是C++的一個關(guān)鍵字,同時也是操作符,關(guān)于new的話題非常多,因為它確實比較復(fù)雜,也非常神秘,這篇文章主要介紹了C++中new關(guān)鍵字用法的相關(guān)資料,需要的朋友可以參考下
    2026-01-01
  • 牛頓迭代法求多項式在1.5附近的值2*x的3次冪--4x平方+3*x-6=0的實現(xiàn)代碼

    牛頓迭代法求多項式在1.5附近的值2*x的3次冪--4x平方+3*x-6=0的實現(xiàn)代碼

    以下代碼是使用了牛頓迭代法求多項式在1.5附近的值 2*x的3次冪 - 4x的平方 + 3*x -6=0的實例。需要的朋友參考下吧
    2013-05-05
  • OpenCV中Grabcut算法的具體使用

    OpenCV中Grabcut算法的具體使用

    本文主要介紹了OpenCV中Grabcut算法的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • vscode ssh遠程連接服務(wù)器一直卡在下載 vscode server問題解決

    vscode ssh遠程連接服務(wù)器一直卡在下載 vscode server問題解決

    在使用vscode使用ssh遠程連接服務(wù)器時,一直卡在下載"vscode 服務(wù)器"階段,本文主要介紹了vscode ssh遠程連接服務(wù)器一直卡在下載 vscode server問題解決,感興趣的可以了解一下
    2025-01-01
  • C語言?超詳細介紹與實現(xiàn)線性表中的無頭單向非循環(huán)鏈表

    C語言?超詳細介紹與實現(xiàn)線性表中的無頭單向非循環(huán)鏈表

    無頭單向非循環(huán)鏈表:結(jié)構(gòu)簡單,一般不會單獨用來存數(shù)據(jù)。實際中更多是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),如哈希桶、圖的鄰接表等等。另外這種結(jié)構(gòu)在筆試面試中出現(xiàn)很多
    2022-03-03

最新評論

岐山县| 中卫市| 镇雄县| 长葛市| 台中市| 伊春市| 虹口区| 望奎县| 枞阳县| 广灵县| 翼城县| 特克斯县| 三门峡市| 浮梁县| 灵武市| 鄂伦春自治旗| 贵阳市| 渑池县| 左权县| 冀州市| 宁强县| 监利县| 文昌市| 洛浦县| 房产| 隆昌县| 当涂县| 铜梁县| 天峻县| 阿拉善左旗| 丽江市| 兰西县| 乌拉特前旗| 广饶县| 琼海市| 柳江县| 屯昌县| 阳谷县| 洪湖市| 双鸭山市| 浮山县|