偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

一、左值引用:從基礎(chǔ)到高級應(yīng)用

1. 左值的本質(zhì)與引用語義

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

  • 避免拷貝:函數(shù)參數(shù)傳遞時直接操作原對象
  • 實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用:返回左值引用支持連續(xù)操作
  • 多態(tài)性支持:基類引用綁定派生類對象

2. const 左值引用的特殊規(guī)則

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

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

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

3. 左值引用的底層實(shí)現(xiàn)

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

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

編譯后的關(guān)鍵指令: (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的內(nèi)存位置(4字節(jié))
    10:         int& rx = x;
00007FF79BEB18E4 48 8D 45 04          lea         rax,[x]   //計算x的內(nèi)存地址,存入rax寄存器
00007FF79BEB18E8 48 89 45 28          mov         qword ptr [rx],rax  //將rax的值(x的地址)存入引用rx的內(nèi)存位置
    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指向的內(nèi)存(即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. 引用與指針的對比分析

特性

引用

指針

空值

不允許

允許

重綁定

不可

可以

內(nèi)存占用

通常優(yōu)化掉

固定大小

安全性

更高

較低

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

1. 右值的分類與特性

C++11 將右值細(xì)分為:

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

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

2. 移動語義的底層實(shí)現(xiàn)

移動構(gòu)造函數(shù)示例:

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

對比拷貝構(gòu)造函數(shù):

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

3. 移動語義的性能優(yōu)勢

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

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

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

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

4. 移動安全與異常處理

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

三、完美轉(zhuǎn)發(fā):模板編程的藝術(shù)

1. 轉(zhuǎn)發(fā)失敗的經(jīng)典案例

考慮轉(zhuǎn)發(fā)函數(shù):

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

當(dāng)傳入右值時,arg變?yōu)樽笾?,?dǎo)致無法調(diào)用移動語義。

2. 萬能引用

模板參數(shù)推導(dǎo)規(guī)則:

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

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

3. 引用折疊規(guī)則全解析

類型推導(dǎo)時的折疊規(guī)則:

聲明的類型

實(shí)際類型

折疊結(jié)果

T& &

左值引用

T&

T& &&

左值引用

T&

T&& &

左值引用

T&

T&& &&

右值引用

T&&

4. std::forward 的魔法實(shí)現(xiàn)

標(biāo)準(zhǔn)庫實(shí)現(xiàn)的核心邏輯:

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

當(dāng)T為左值引用時,static_cast轉(zhuǎn)換為左值引用;否則轉(zhuǎn)換為右值引用。

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

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

①  基礎(chǔ)定義

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&&都轉(zhuǎn)換為int。

② 在forward中的應(yīng)用

觀察函數(shù)參數(shù)聲明:

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

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

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

類型推導(dǎo)過程:

原始類型

std::remove_reference::type

最終類型

int

int

int&

int&

int

int&

int&&

int

int&

const int&

const int

const int&

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

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

這樣設(shè)計是為了保證:

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

(2) 第二:static_cast的魔法

① 引用折疊規(guī)則

C++的引用折疊規(guī)則是理解這個轉(zhuǎn)換的關(guān)鍵:

模板參數(shù) T 的原始類型

T&&

int&

int& &&

int&&

int&& &&

int

int&&

② 實(shí)際轉(zhuǎn)換過程

我們分情況看下:

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

// 假設(shè)調(diào)用:forward<int&>(x)
T = int&
static_cast<T&&> → static_cast<int& &&> → static_cast<int&>

結(jié)果返回左值引用。

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

// 假設(shè)調(diào)用:forward<int&&>(x)
T = int&&
static_cast<T&&> → static_cast<int&& &&> → static_cast<int&&>

結(jié)果返回右值引用。

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

// 假設(shè)調(diào)用:forward<int>(x)
T = int
static_cast<T&&> → static_cast<int&&>

結(jié)果返回右值引用。

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

① 左值轉(zhuǎn)發(fā)場景

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

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

② 右值轉(zhuǎn)發(fā)場景

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

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

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

① 保持值類別

參數(shù)arg在函數(shù)內(nèi)部始終是左值(因?yàn)楹瘮?shù)參數(shù)都是左值)。通過static_cast:

  • 當(dāng)原始參數(shù)是左值時,返回左值引用
  • 當(dāng)原始參數(shù)是右值時,返回右值引用

② 完美轉(zhuǎn)發(fā)的必要性

沒有std::forward時:

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

即使傳入右值,arg在函數(shù)內(nèi)部也是左值,導(dǎo)致無法觸發(fā)移動語義。

使用std::forward后:

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

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

要素

作用

remove_reference

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

T&&

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

static_cast

執(zhí)行有條件的類型轉(zhuǎn)換:T 含左值信息則返回左值,否則返回右值

函數(shù)參數(shù)設(shè)計為左值

避免函數(shù)簽名中出現(xiàn)右值引用參數(shù),符合 C++函數(shù)參數(shù)傳遞規(guī)則

通過這種精妙的設(shè)計,std::forward能夠:

  • 根據(jù)模板參數(shù)T攜帶的類型信息
  • 智能判斷應(yīng)該返回左值還是右值引用
  • 在編譯期完成所有類型轉(zhuǎn)換
  • 實(shí)現(xiàn)真正的完美轉(zhuǎn)發(fā)

這正是 C++模板元編程和類型系統(tǒng)的精華所在,也是現(xiàn)代 C++高效資源管理的基礎(chǔ)。

③ 完美轉(zhuǎn)發(fā)的實(shí)戰(zhàn)應(yīng)用

工廠函數(shù)實(shí)現(xiàn):

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));

四、完美轉(zhuǎn)發(fā)使用示例

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

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

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

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

private:
    std::string data;
};

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

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

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

    return0;
}

輸出結(jié)果:

拷貝構(gòu)造: Hello
移動構(gòu)造: World
移動構(gòu)造: C++11

1. 關(guān)鍵解釋

std::forward(arg):

  • 當(dāng) arg 是左值時,std::forward 返回左值引用,觸發(fā) Widget 的拷貝構(gòu)造函數(shù)。
  • 當(dāng) arg 是右值時,std::forward 返回右值引用,觸發(fā) Widget 的移動構(gòu)造函數(shù)。

模板參數(shù) T&&:

  • 這是"萬能引用",可以綁定到左值或右值。
  • 配合 std::forward 實(shí)現(xiàn)參數(shù)類型的完美轉(zhuǎn)發(fā)。

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

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

  • 所有情況調(diào)用拷貝構(gòu)造(性能損失)。
  • 無法利用移動語義優(yōu)化。

責(zé)任編輯:趙寧寧 來源: CppPlayer
相關(guān)推薦

2020-08-11 11:00:16

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

2012-02-13 10:18:42

C++ 11

2022-07-26 00:36:06

C#C++函數(shù)

2022-02-16 12:52:22

C++項(xiàng)目編譯器

2025-06-06 07:35:06

C++表達(dá)式右值

2009-11-12 09:37:14

Visual Stud

2023-07-17 10:28:00

C/C++編程接口

2024-03-05 09:55:00

C++右值引用開發(fā)

2025-02-07 09:58:43

C++11Lvalue對象

2010-02-03 17:32:54

C++左值與右值

2025-03-10 08:30:00

2011-05-24 17:20:57

程序員

2020-08-05 07:53:53

程序員網(wǎng)站技術(shù)

2012-11-08 09:49:30

C++Java程序員

2024-05-21 13:41:17

2022-09-30 08:16:38

令牌客戶端隱藏式

2023-02-13 23:43:06

程序員網(wǎng)站

2023-11-29 09:47:11

C++對象

2011-03-30 17:20:18

C++引用

2024-01-18 10:27:30

C++引用函數(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號