成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C++程序員必須要懂的:左值引用、右值引用、完美轉發!長文慎入,建議收藏!

開發
若直接傳遞 arg(即 return Widget(arg);),則無論原始參數是左值還是右值,arg 都會被視為左值。

一、左值引用:從基礎到高級應用

1. 左值的本質與引用語義

左值(Lvalue)是具名對象(Named Object),具有明確的內存地址和生命周期。左值引用(T&)本質上是變量的"別名",其核心價值在于:

  • 避免拷貝:函數參數傳遞時直接操作原對象
  • 實現鏈式調用:返回左值引用支持連續操作
  • 多態性支持:基類引用綁定派生類對象

2. const 左值引用的特殊規則

const T&的獨特之處在于可綁定右值,這一特性被廣泛用于:

  • 接受字面量或臨時對象作為參數
  • 延長臨時對象的生命周期
void process(const std::string& s); 

process("hello");  // 合法,構造臨時string對象

3. 左值引用的底層實現

從匯編角度看,引用本質是自動解引用的指針。以下代碼:

int x = 10;
int& rx = x;
rx = 20;

編譯后的關鍵指令: (VS2022)

6: int main()
     7: {
00007FF79BEB18A0 40 55                push        rbp  
00007FF79BEB18A2 57                   push        rdi  
00007FF79BEB18A3 48 81 EC 28 01 00 00 sub         rsp,128h  
00007FF79BEB18AA 48 8D 6C 24 20       lea         rbp,[rsp+20h]  
00007FF79BEB18AF 48 8D 7C 24 20       lea         rdi,[rsp+20h]  
00007FF79BEB18B4 B9 12 00 00 00       mov         ecx,12h  
00007FF79BEB18B9 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
00007FF79BEB18BE F3 AB                rep stos    dword ptr [rdi]  
00007FF79BEB18C0 48 8B 05 39 A7 00 00 mov         rax,qword ptr [__security_cookie (07FF79BEBC000h)]  
00007FF79BEB18C7 48 33 C5             xor         rax,rbp  
00007FF79BEB18CA 48 89 85 F8 00 00 00 mov         qword ptr [rbp+0F8h],rax  
00007FF79BEB18D1 48 8D 0D 93 F7 00 00 lea         rcx,[__10633CA5_0904@cpp (07FF79BEC106Bh)]  
00007FF79BEB18D8 E8 7F FA FF FF       call        __CheckForDebuggerJustMyCode (07FF79BEB135Ch)  
     8: 
     9:         int x = 10;
00007FF79BEB18DD C7 45 04 0A 00 00 00 mov         dword ptr [x],0Ah   // 將10(0x0A)存入變量x的內存位置(4字節)
    10:         int& rx = x;
00007FF79BEB18E4 48 8D 45 04          lea         rax,[x]   //計算x的內存地址,存入rax寄存器
00007FF79BEB18E8 48 89 45 28          mov         qword ptr [rx],rax  //將rax的值(x的地址)存入引用rx的內存位置
    11:         rx = 20;
00007FF79BEB18EC 48 8B 45 28          mov         rax,qword ptr [rx]  //從rx中讀取x的地址到rax
00007FF79BEB18F0 C7 00 14 00 00 00    mov         dword ptr [rax],14h  //將20(0x14)寫入rax指向的內存(即x)
    12: 
    13:         return 0 ;
00007FF79BEB18F6 33 C0                xor         eax,eax  
    14: }

這里有個小知識:dword ptr 和 qword ptr。

每個 word 是 16 位,所以 DWORD 就是雙字,即 32 位。QWORD 則是四個字,也就是 64 位。因此,dword ptr 用于 32 位操作,qword ptr 用于 64 位操作。

4. 引用與指針的對比分析

特性

引用

指針

空值

不允許

允許

重綁定

不可

可以

內存占用

通常優化掉

固定大小

安全性

更高

較低

二、右值引用:移動語義的革命

1. 右值的分類與特性

C++11 將右值細分為:

  • 純右值(prvalue):字面量、表達式結果
  • 將亡值(xvalue):即將被移動的對象
int&& r1 = 5;         // prvalue
std::move(a);         // 將左值轉為將亡值

int func() { return 5; }
func();                 // 返回非引用的函數調用是純右值

2. 移動語義的底層實現

移動構造函數示例:

Buffer(Buffer&& other) noexcept 
    : data_(nullptr), size_(0) {  // 初始化為空狀態
    if (this != &other) {
        data_ = other.data_;
        size_ = other.size_;
        other.data_ = nullptr;
        other.size_ = 0;
    }
}

對比拷貝構造函數:

Buffer(const Buffer& other) 
    : data_(new char[other.size_]), size_(other.size_) {
    std::memcpy(data_, other.data_, size_);
}

3. 移動語義的性能優勢

通過std::vector的插入操作對比:

// 拷貝語義版本
std::vector<BigObject> vec;
BigObject obj;
vec.push_back(obj);  // 深拷貝發生

// 移動語義版本
vec.push_back(std::move(obj)); // 僅指針交換

性能測試顯示,對于包含 1MB 數據的對象,移動操作比拷貝快 1000 倍以上。

4. 移動安全與異常處理

  • 使用noexcept聲明移動操作
  • 在移動后將被移對象置為有效狀態
class Resource {
public:
    Resource(Resource&& other) noexcept 
        : handle_(other.handle_) {
        other.handle_ = nullptr; // 確保安全
    }
    
private:
    void* handle_;
};

三、完美轉發:模板編程的藝術

1. 轉發失敗的經典案例

考慮轉發函數:

template<typename T>
void bad_forward(T arg) {
    target(arg);  // 丟失值類別信息
}

當傳入右值時,arg變為左值,導致無法調用移動語義。

2. 萬能引用

模板參數推導規則:

template<typename T>
void func(T&& param);  // T&&可能是左值或右值引用

int x = 10;
func(x);   // T推導為int&,折疊為int&
func(10);  // T推導為int,最終類型int&&

3. 引用折疊規則全解析

類型推導時的折疊規則:

聲明的類型

實際類型

折疊結果

T& &

左值引用

T&

T& &&

左值引用

T&

T&& &

左值引用

T&

T&& &&

右值引用

T&&

4. std::forward 的魔法實現

標準庫實現的核心邏輯:

template<typename T>
T&& forward(typename std::remove_reference<T>::type& arg) {
    return static_cast<T&&>(arg);
}

當T為左值引用時,static_cast轉換為左值引用;否則轉換為右值引用。

這里怎么理解呢? 代碼中是怎么做到的? remove_reference 又是什么意思?

(1) 第一:std::remove_reference的作用

①  基礎定義

std::remove_reference是類型特征(type trait),去除類型 T 的所有引用修飾符(無論 T 是 T& 還是 T&&)。

如果 T = int& → std::remove_reference<T>::type 為 int
如果 T = int&& → std::remove_reference<T>::type 為 int
如果 T = int → std::remove_reference<T>::type 為 int

它會將int&或int&&都轉換為int。

② 在forward中的應用

觀察函數參數聲明:

typename std::remove_reference<T>::type& arg

typename std::remove_reference::type& 的含義:

將去除了引用后的類型 重新添加左值引用,最終得到的是一個 左值引用類型,但引用的底層類型是原始的非引用類型。(非引用類型的左值引用 )

類型推導過程:

原始類型

std::remove_reference::type

最終類型

int

int

int&

int&

int

int&

int&&

int

int&

const int&

const int

const int&

這里的arg被強制聲明為非引用類型的左值引用。例如:

  • 若T = int&,則arg類型為int& → remove_reference得到int → arg是int&
  • 若T = int&&,則arg類型為int&& → remove_reference得到int → arg仍是int&

這樣設計是為了保證:

  • 參數始終是左值引用(避免函數參數類型出現右值引用)
  • 剝離原有引用,為后續的引用折疊做準備

(2) 第二:static_cast的魔法

① 引用折疊規則

C++的引用折疊規則是理解這個轉換的關鍵:

模板參數 T 的原始類型

T&&

int&

int& &&

int&&

int&& &&

int

int&&

② 實際轉換過程

我們分情況看下:

情況 1:當 T 是左值引用(如int&)

// 假設調用:forward<int&>(x)
T = int&
static_cast<T&&> → static_cast<int& &&> → static_cast<int&>

結果返回左值引用。

情況 2:當 T 是右值引用(如int&&)

// 假設調用:forward<int&&>(x)
T = int&&
static_cast<T&&> → static_cast<int&& &&> → static_cast<int&&>

結果返回右值引用。

情況 3:當 T 是非引用(如int)

// 假設調用:forward<int>(x)
T = int
static_cast<T&&> → static_cast<int&&>

結果返回右值引用。

(3) 第三:完整推導過程示例

① 左值轉發場景

int x = 10;
forward<int&>(x);

// 模板實例化:
int& && forward(int& arg) {
    return static_cast<int&>(arg); 
}
// 折疊后:
int& forward(int& arg) { return arg; }

② 右值轉發場景

forward<int&&>(std::move(x));

// 模板實例化:
int&& && forward(int& arg) {
    return static_cast<int&&>(arg);
}
// 折疊后:
int&& forward(int& arg) { return static_cast<int&&>(arg); }

(4) 第四:為什么要這樣設計?

① 保持值類別

參數arg在函數內部始終是左值(因為函數參數都是左值)。通過static_cast:

  • 當原始參數是左值時,返回左值引用
  • 當原始參數是右值時,返回右值引用

② 完美轉發的必要性

沒有std::forward時:

template<typename T>
void wrapper(T&& arg) {
    target(arg);  // arg總是左值
}

即使傳入右值,arg在函數內部也是左值,導致無法觸發移動語義。

使用std::forward后:

template<typename T>
void wrapper(T&& arg) {
    target(std::forward<T>(arg)); 
}

可以保持原始參數的值類別(左值/右值)。

要素

作用

remove_reference

保證函數參數類型為基本類型的左值引用,剝離原有引用信息

T&&

根據模板參數 T 的原始類型,通過引用折疊決定最終返回類型

static_cast

執行有條件的類型轉換:T 含左值信息則返回左值,否則返回右值

函數參數設計為左值

避免函數簽名中出現右值引用參數,符合 C++函數參數傳遞規則

通過這種精妙的設計,std::forward能夠:

  • 根據模板參數T攜帶的類型信息
  • 智能判斷應該返回左值還是右值引用
  • 在編譯期完成所有類型轉換
  • 實現真正的完美轉發

這正是 C++模板元編程和類型系統的精華所在,也是現代 C++高效資源管理的基礎。

③ 完美轉發的實戰應用

工廠函數實現:

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

// 使用示例
auto p = create<std::unique_ptr<int>>(new int(5));

四、完美轉發使用示例

以下是一個使用 std::forward 的示例,演示如何在模板函數中實現參數的完美轉發,保留原始值類別(左值/右值):

#include <iostream>
#include <string>
#include <utility>

// 示例類:記錄構造方式
classWidget {
public:
    // 拷貝構造(左值)
    Widget(const std::string& s) : data(s) {
        std::cout << "拷貝構造: " << data << std::endl;
    }

    // 移動構造(右值)
    Widget(std::string&& s) : data(std::move(s)) {
        std::cout << "移動構造: " << data << std::endl;
    }

private:
    std::string data;
};

// 工廠函數:完美轉發參數到 Widget 的構造函數
template<typename T>
Widget createWidget(T&& arg){
    // 使用 std::forward 保留 arg 的原始值類別(左值/右值)
    returnWidget(std::forward<T>(arg));
}

intmain(){
    std::string str = "Hello";

    // 傳遞左值 → 調用拷貝構造
    Widget w1 = createWidget(str);
    
    // 傳遞右值(臨時對象)→ 調用移動構造
    Widget w2 = createWidget(std::string("World"));
    
    // 傳遞字符串字面量 → 直接構造為右值(無需拷貝)
    Widget w3 = createWidget("C++11");

    return0;
}

輸出結果:

拷貝構造: Hello
移動構造: World
移動構造: C++11

1. 關鍵解釋

std::forward(arg):

  • 當 arg 是左值時,std::forward 返回左值引用,觸發 Widget 的拷貝構造函數。
  • 當 arg 是右值時,std::forward 返回右值引用,觸發 Widget 的移動構造函數。

模板參數 T&&:

  • 這是"萬能引用",可以綁定到左值或右值。
  • 配合 std::forward 實現參數類型的完美轉發。

2. 對比:如果不使用 std::forward

若直接傳遞 arg(即 return Widget(arg);),則無論原始參數是左值還是右值,arg 都會被視為左值,導致:

  • 所有情況調用拷貝構造(性能損失)。
  • 無法利用移動語義優化。

責任編輯:趙寧寧 來源: CppPlayer
相關推薦

2020-08-11 11:00:16

左值引用右值引用移動語義

2012-02-13 10:18:42

C++ 11

2022-07-26 00:36:06

C#C++函數

2022-02-16 12:52:22

C++項目編譯器

2025-06-06 07:35:06

C++表達式右值

2009-11-12 09:37:14

Visual Stud

2023-07-17 10:28:00

C/C++編程接口

2024-03-05 09:55:00

C++右值引用開發

2025-02-07 09:58:43

C++11Lvalue對象

2010-02-03 17:32:54

C++左值與右值

2011-05-24 17:20:57

程序員

2025-03-10 08:30:00

2020-08-05 07:53:53

程序員網站技術

2012-11-08 09:49:30

C++Java程序員

2024-05-21 13:41:17

2023-11-29 09:47:11

C++對象

2023-02-13 23:43:06

程序員網站

2009-08-19 16:39:44

C#值類型C#引用類型

2009-08-26 14:05:19

C#值類型和引用類型

2011-03-30 17:20:18

C++引用
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人免费视频在线观看 | 91.xxx.高清在线 | 国产精品亚洲成在人线 | 国产精品久久久久无码av | 国产综合精品 | 亚洲一区二区精品视频在线观看 | 亚洲一区二区免费 | 啪啪网页 | 妖精视频一区二区三区 | 成人免费一区二区 | 中文字幕日韩欧美一区二区三区 | 四虎免费视频 | av一区二区三区四区 | 欧日韩在线观看 | 91一区二区三区在线观看 | 亚洲激情网站 | 精品久久久久一区二区国产 | 亚洲一区黄色 | 久久一日本道色综合久久 | 久久国产精品一区二区三区 | 久久久国产精品一区 | 夜夜爽99久久国产综合精品女不卡 | 国产精品久久久久久久久久三级 | 中文字幕欧美一区 | 久久国产婷婷国产香蕉 | 欧美日韩精品综合 | 中文字幕高清av | 国产一区二区影院 | 日韩欧美在线观看一区 | 欧美黄色一级毛片 | 免费一级欧美在线观看视频 | 欧美在线观看一区二区 | 色视频网站在线观看 | 本地毛片| 久久精品亚洲欧美日韩久久 | 亚洲精品电影在线观看 | 国产精品我不卡 | 精品久久久久久亚洲综合网站 | 精精国产xxxx视频在线播放 | 资源首页二三区 | 亚洲成人av |