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

全面掌握 C++ && 的兩種模式及其應(yīng)用場(chǎng)景

開(kāi)發(fā) 前端
右值引用&& 是 C++11 的性能優(yōu)化利器,專(zhuān)門(mén)處理“一次性用品”,配合 std::move 實(shí)現(xiàn)高效的資源轉(zhuǎn)移。萬(wàn)能引用&& 是泛型編程和完美轉(zhuǎn)發(fā)的核心,它像個(gè)變色龍,能適應(yīng)并保持參數(shù)的原始“價(jià)值”,配合 std::forward 確保信息無(wú)損傳遞。?

今天,來(lái)扒一扒 C++ 里容易讓人理解混淆的 && 符號(hào),特別是它在“萬(wàn)能引用”和“右值引用”這兩個(gè)身份間反復(fù)橫跳的騷操作。

第一:右值引用 - "一次性"道具的專(zhuān)屬接收器

在 C++11 這個(gè)偉大的版本問(wèn)世之前,我們 C++ 程序員過(guò)著相對(duì)"樸素"的生活。

對(duì)象要么是"有名有姓"的左值(Lvalue),比如 int a = 10; 里的 a,你可以反復(fù)用它的名字找到它,給它賦值,取它的地址,就像你家養(yǎng)的那只可以擼可以喂、隨時(shí)能找到的貓。

要么就是曇花一現(xiàn)的右值(Rvalue),比如 10、a + b 的結(jié)果、函數(shù)返回的臨時(shí)對(duì)象 getString()。它們就像你在路邊撿到的、用完就可能消失的優(yōu)惠券,或者外賣(mài)送的一次性筷子,用完就扔,通常沒(méi)有名字,也不能(或者不應(yīng)該)對(duì)它們進(jìn)行修改。

拷貝這些"一次性用品"往往是浪費(fèi)的。比如你寫(xiě) std::string s = getString();,如果 getString() 返回一個(gè)臨時(shí)的 std::string 對(duì)象(右值),老版本的 C++ 會(huì)傻乎乎地把這個(gè)臨時(shí)對(duì)象里的數(shù)據(jù)(比如一大段文字)完完整整地復(fù)制一份到新的 s 對(duì)象里,然后那個(gè)臨時(shí)對(duì)象就被銷(xiāo)毀了。這就像你點(diǎn)外賣(mài),人家送來(lái)一份用精美一次性餐盒裝的飯,你非得把它小心翼翼地倒進(jìn)你自己的碗里,然后把那個(gè)還能用的餐盒扔掉... 何必呢?

于是,C++11 帶來(lái)了右值引用,語(yǔ)法就是 類(lèi)型&&。它的核心使命只有一個(gè):綁定到右值!

void process_disposable(std::string&& disposable_cup){
    // 這里的 disposable_cup 明確表示:我只接收那些“一次性”的 string!
    std::cout << "Processing the disposable cup's content: " << disposable_cup << std::endl;
    // 重點(diǎn)來(lái)了:我可以“偷”走它的資源!
    std::string my_permanent_mug = std::move(disposable_cup); // 資源轉(zhuǎn)移,杯子空了
    std::cout << "Content moved to my mug: " << my_permanent_mug << std::endl;
    // 注意:disposable_cup 現(xiàn)在可能為空了,不能再依賴(lài)它的內(nèi)容了
}

intmain(){
    std::string permanent_bottle = "Water";
    // process_disposable(permanent_bottle); // 編譯錯(cuò)誤!人家不要你的“永久水瓶”(左值)
    process_disposable("Juice"); // OK!"Juice" 是個(gè)臨時(shí)字符串(右值)
    process_disposable(std::string("Milk")); // OK!std::string("Milk") 創(chuàng)建臨時(shí)對(duì)象(右值)
    
    std::string another_bottle = "Soda";
    // 如果你非要把你的永久水瓶當(dāng)一次性的給,需要顯式“打包”
    process_disposable(std::move(another_bottle)); // OK!std::move 把它偽裝成右值
    // 但要小心,another_bottle 的內(nèi)容可能被“偷”走了!
    
    return0;
}

生活案例:右值引用就像是"二手閑置物品接收點(diǎn)"

想象一下,你家小區(qū)門(mén)口有個(gè)牌子寫(xiě)著:“閑置物品(即將丟棄)接收點(diǎn),聯(lián)系人:張三 &&”。

  • 規(guī)則:這個(gè)接收點(diǎn)(張三&&)只接收那些你明確表示“我不要了,準(zhǔn)備扔了”的東西(右值)。比如你剛喝完的一次性飲料瓶、過(guò)期的雜志、穿不了的舊衣服。
  • 好處:張三(右值引用)拿到這些東西后,可以“廢物利用”,比如把瓶子拿去賣(mài)錢(qián),把雜志內(nèi)容剪下來(lái)做手工,把舊衣服拆了做抹布(對(duì)應(yīng) C++ 的移動(dòng)語(yǔ)義 ,轉(zhuǎn)移資源而不是拷貝)。他知道這些東西的原主人不打算再要了,所以可以大膽地“破壞性”使用。
  • 限制:你不能把你家祖?zhèn)鞯?、還在用的電視機(jī)(左值 my_tv)直接搬過(guò)去給張三,他會(huì)拒收,說(shuō):“嘿!這玩意兒你還用呢,我不能收!”. 除非你鄭重聲明:“這電視我確實(shí)不要了!給你了!”,相當(dāng)于你對(duì)電視機(jī)用了 std::move(my_tv),把它“標(biāo)記”為可以被接收的狀態(tài)。但一旦你這么做了,就別指望回家還能看這臺(tái)電視了,它的“靈魂”(資源)可能已經(jīng)被張三搬走了。

總結(jié)一下右值引用:

  • 語(yǔ)法:類(lèi)型&& (在類(lèi)型 不是 模板參數(shù)推導(dǎo)上下文,或者 auto&& 推導(dǎo)上下文時(shí))
  • 作用:專(zhuān)門(mén)綁定到右值。
  • 目的:實(shí)現(xiàn)移動(dòng)語(yǔ)義,避免不必要的拷貝,提升性能。就像那個(gè)只收閑置品的張三,高效利用資源。

第二:萬(wàn)能引用 - "百變星君"的身份魔法

再說(shuō)到萬(wàn)能引用!它是 C++ 界的“百變星君”,它也用 && 符號(hào),但玩法完全不同!

萬(wàn)能引用,由 Scott Meyers 大神提出,雖然現(xiàn)在官方和很多開(kāi)發(fā)者更傾向于叫它 轉(zhuǎn)發(fā)引用,但“萬(wàn)能引用”這個(gè)名字實(shí)在太形象了,我們先用著,后面再?gòu)?qiáng)調(diào)它的核心使命是“轉(zhuǎn)發(fā)”。

萬(wàn)能引用只在特定的上下文中出現(xiàn),滿(mǎn)足以下兩個(gè)條件時(shí),T&& 才不是右值引用,而是萬(wàn)能引用:

1. 發(fā)生在模板類(lèi)型推導(dǎo)中:函數(shù)模板的參數(shù)類(lèi)型是 T&&,其中 T 是需要推導(dǎo)的模板參數(shù)。

template<typename T>
void magic_box(T&& item) { // <--- 這里的 T&& 就是萬(wàn)能引用!
    // ... 魔法操作 ...
}

2. 發(fā)生在 auto類(lèi)型推導(dǎo)中:變量聲明使用 auto&&。

auto&& magic_variable = some_expression; // <--- 這里的 auto&& 也是萬(wàn)能引用!

關(guān)鍵區(qū)別:看到?jīng)]?類(lèi)型推導(dǎo)!這就是區(qū)分它是“專(zhuān)一的右值引用”還是“百變的萬(wàn)能引用”的唯一標(biāo)準(zhǔn)!

1、如果 && 所在的類(lèi)型涉及到編譯器的類(lèi)型推導(dǎo)(typename T 或 auto),那它就是萬(wàn)能引用;

2、如果類(lèi)型是寫(xiě)死的(比如 std::string&&),那就是右值引用。

那么,“萬(wàn)能”體現(xiàn)在哪里呢?

萬(wàn)能引用之所以“萬(wàn)能”,是因?yàn)樗瓤梢越壎ǖ阶笾担部梢越壎ǖ接抑?!?jiǎn)直是通吃!

  • 當(dāng)你傳遞一個(gè)左值給萬(wàn)能引用時(shí),模板參數(shù) T 會(huì)被推導(dǎo)為左值引用類(lèi)型(例如 int&),然后根據(jù) C++ 的引用折疊規(guī)則,T&& (即 int& &&)會(huì)折疊成 int&(左值引用)。
  • 當(dāng)你傳遞一個(gè)右值給萬(wàn)能引用時(shí),模板參數(shù) T 會(huì)被推導(dǎo)為普通類(lèi)型(例如 int),T&& (即 int&&)就保持為 int&&(右值引用)。
  • 記?。和茖?dǎo)的結(jié)果只有兩個(gè):左值引用或者普通類(lèi)型,沒(méi)有右值引用

轉(zhuǎn)發(fā)引用這套特殊的類(lèi)型推導(dǎo)規(guī)則總結(jié):

  • 規(guī)則 1:如果傳遞給 T&& 的實(shí)參是一個(gè)左值 (Lvalue) ,類(lèi)型為 U,那么 T 會(huì)被推導(dǎo)為 U& (左值引用類(lèi)型)。
  • 規(guī)則 2:如果傳遞給 T&& 的實(shí)參是一個(gè)右值 (Rvalue) ,類(lèi)型為 U,那么 T 會(huì)被推導(dǎo)為 U (原始非引用類(lèi)型)。

引用折疊規(guī)則小抄(記住這個(gè),你就掌握了萬(wàn)能引用的核心秘密):

  • T& & -> T& (左引用 的 左引用 還是 左引用)
  • T& && -> T& (左引用 的 右引用 變成 左引用)
  • T&& & -> T& (右引用 的 左引用 變成 左引用)
  • T&& && -> T&& (右引用 的 右引用 還是 右引用)

簡(jiǎn)單記: 

1、只要有 &(左值引用)參與折疊,結(jié)果就是 &(左值引用)。 

2、只有 && 和 &&碰頭,結(jié)果才是 &&(右值引用)。

3、推導(dǎo)是針對(duì) T 進(jìn)行,引用折疊是針對(duì)參數(shù)進(jìn)行,先進(jìn)行推導(dǎo),然后拿推導(dǎo)出的 T 對(duì)參數(shù)進(jìn)行引用折疊,得到最后的值

看個(gè)例子:

#include <iostream>
#include <string>
#include <utility> // 為了 std::forward

voidprocess_further(const std::string& s){
    std::cout << "Processing as LValue (const ref): " << s << std::endl;
}

voidprocess_further(std::string&& s){
    std::cout << "Processing as RValue (move): " << s << std::endl;
    // 可以在這里移動(dòng)資源 s
}

template<typename T>
voidmagic_box(T&& item){
    std::cout << "Inside magic_box: ";
    // 僅僅打印類(lèi)型不夠直觀,我們后面會(huì)看怎么用它
    // 關(guān)鍵點(diǎn):無(wú)論傳入的是左值還是右值,item 在 magic_box 函數(shù)內(nèi)部,
    // 因?yàn)樗忻至?,所以它本身是一個(gè)左值!
    // just_print(item); // 如果直接傳遞 item,總是傳遞左值

    // 正確的做法是“完美轉(zhuǎn)發(fā)”!
    process_further(std::forward<T>(item));
}

intmain(){
    std::string lv_string = "I am an LValue";
    magic_box(lv_string); // 傳入左值

    magic_box("I am an RValue"); // 傳入右值 (字符串字面量轉(zhuǎn)臨時(shí) string)
    magic_box(std::string("Another RValue")); // 傳入右值 (臨時(shí) string 對(duì)象)

    std::string another_lv = "One more LValue";
    magic_box(std::move(another_lv)); // 傳入被 std::move 轉(zhuǎn)換的右值

    return0;
}

生活案例:萬(wàn)能引用就像是“萬(wàn)能快遞代收點(diǎn)”

想象一下,你家小區(qū)新開(kāi)了一個(gè)快遞代收點(diǎn),招牌是:“快遞代收,聯(lián)系人:李四 <模板 T> &&”。

規(guī)則:

這個(gè)李四(T&&)非常靈活,不管是是否保價(jià)(保價(jià):左值,普通:右值)的快遞(T),他都能代收。

怎么做到的?

當(dāng)你送來(lái)一個(gè)保價(jià)包裹(左值 )時(shí),李四心里會(huì)記下:“哦,這是個(gè)保價(jià)物品(T 推導(dǎo)為 Package&),我得按保價(jià)物品(Package&)的方式保管?!?/p>

當(dāng)你送來(lái)一個(gè)普通包裹(右值)時(shí),他記下:“嗯,這是個(gè)普通件(T 推導(dǎo)為 Package),按普通件(Package&&)處理就行?!?(這就是類(lèi)型推導(dǎo) + 引用折疊)

核心價(jià)值(即將引出完美轉(zhuǎn)發(fā)):

李四代收了快遞后,他的工作還沒(méi)完。他最終要把快遞交給你(或者你指定的下一個(gè)人)。這時(shí),他必須 原封不動(dòng)地 告訴你這個(gè)快遞 最初 是個(gè)保價(jià)件還是普通件。他不能把所有收到的快遞都當(dāng)成普通件(就像函數(shù)內(nèi)部參數(shù) item 總是左值一樣),也不能都當(dāng)保價(jià)件。他需要一個(gè)方法來(lái)“恢復(fù)”快遞的原始屬性。

第三:完美轉(zhuǎn)發(fā) - “信使”的神圣使命

我們從上面的 magic_box 例子看到,萬(wàn)能引用 T&& item 雖然能接收左值和右值,但在函數(shù) magic_box 內(nèi)部,item 這個(gè)參數(shù)本身,因?yàn)樗辛嗣郑妥兂闪艘粋€(gè)左值!

這就帶來(lái)一個(gè)問(wèn)題:如果 magic_box 的目的是要把接收到的 item 原封不動(dòng)地(保持其原始的左值或右值屬性)傳遞給另一個(gè)函數(shù)(比如上面例子中的 process_further),直接傳遞 item 就不行了,因?yàn)?nbsp;item 已經(jīng)是左值了。

這就是 完美轉(zhuǎn)發(fā)(Perfect Forwarding) 的用武之地,而實(shí)現(xiàn)它的工具就是 std::forward。

std::forward(item) 的作用就是:根據(jù)模板參數(shù) T 被推導(dǎo)出的原始類(lèi)型(是 int& 還是 int),將左值 item 轉(zhuǎn)換回它對(duì)應(yīng)的原始值類(lèi)別

1、如果當(dāng)初傳入 magic_box 的是左值,T 推導(dǎo)為 Type&,std::forward(item) 會(huì)返回一個(gè)左值引用。

2、如果當(dāng)初傳入 magic_box 的是右值,T 推導(dǎo)為 Type,std::forward(item) 會(huì)返回一個(gè)右值引用。

所以,萬(wàn)能引用的標(biāo)準(zhǔn)用法幾乎總是和 std::forward 成對(duì)出現(xiàn),像這樣:

template<typename T>
void forwarding_function(T&& arg) {
    // ... 可能有一些自己的邏輯 ...
    
    // 把 arg 完美轉(zhuǎn)發(fā)給下一個(gè)函數(shù)
    callee_function(std::forward<T>(arg)); 
}

生活案例:“萬(wàn)能快遞代收點(diǎn)”的終極形態(tài)

李四(T&&)的代收點(diǎn)現(xiàn)在升級(jí)了:

接收:他能接收任何類(lèi)型的快遞(萬(wàn)能引用 T&&),并根據(jù)快遞是保價(jià)(左值)還是普通(右值)在小本本上記錄下原始類(lèi)型(模板推導(dǎo) T 為 Type& 或 Type)。

內(nèi)部處理:在他代收點(diǎn)內(nèi)部,所有快遞暫時(shí)都放在一個(gè)“已接收”區(qū)域(參數(shù) item 成為左值)。

轉(zhuǎn)發(fā):當(dāng)他要把快遞交給最終收件人或下一站時(shí),他會(huì)查小本本(看 T 的類(lèi)型),然后使用一個(gè)特殊的“轉(zhuǎn)發(fā)標(biāo)簽”(std::forward),告訴對(duì)方:“這個(gè)快遞,請(qǐng)按照它原本是保價(jià)還是普通的屬性來(lái)處理!”(完美轉(zhuǎn)發(fā))。

這樣,無(wú)論快遞經(jīng)歷了多少次代收(函數(shù)調(diào)用鏈),只要每一站都使用萬(wàn)能引用和完美轉(zhuǎn)發(fā),快遞的原始“身份”(左值/右值屬性)就能一直保持下去,直到它被最終消費(fèi)(比如被移動(dòng)構(gòu)造或拷貝構(gòu)造)。

第四:總結(jié)與區(qū)分

特性

右值引用 

萬(wàn)能引用/轉(zhuǎn)發(fā)引用 

語(yǔ)法形式

類(lèi)型&&

T&& (T 是模板參數(shù)) 或 auto&&

關(guān)鍵條件

類(lèi)型是確定的,沒(méi)有類(lèi)型推導(dǎo)參與

必須發(fā)生在模板類(lèi)型推導(dǎo)或auto&&推導(dǎo)上下文中

綁定對(duì)象

只能綁定到右值

既能綁定到左值,也能綁定到右值

推導(dǎo)行為

無(wú)類(lèi)型推導(dǎo)

傳入左值時(shí),T推導(dǎo)為Type&;傳入右值時(shí),T推導(dǎo)為Type

引用折疊

不涉及(因?yàn)轭?lèi)型固定)

核心機(jī)制!Type& && -> Type&Type&& && -> Type&&

主要目的

實(shí)現(xiàn)移動(dòng)語(yǔ)義,優(yōu)化資源轉(zhuǎn)移

實(shí)現(xiàn)完美轉(zhuǎn)發(fā),保持值類(lèi)別在函數(shù)調(diào)用鏈中傳遞

常用搭檔

std::move (用于將左值轉(zhuǎn)為右值以供綁定)

std::forward (用于在函數(shù)內(nèi)部恢復(fù)原始值類(lèi)別進(jìn)行轉(zhuǎn)發(fā))

生活類(lèi)比

閑置物品接收點(diǎn)(只收不要的)

萬(wàn)能快遞代收點(diǎn)(啥都收,且能保持原始狀態(tài)轉(zhuǎn)發(fā))

如何一眼區(qū)分?

記住這個(gè)口訣:

模板推導(dǎo) 或 auto, && 變身萬(wàn)能佬;類(lèi)型寫(xiě)死 不推導(dǎo), &&就是右值寶。

當(dāng)看到 T&& 或 auto&& 時(shí),問(wèn)自己:“這里的 T 或 auto 是不是正在被編譯器推導(dǎo)出來(lái)?”

如果是,它就是萬(wàn)能引用。

如果不是,比如 void func(std::string&& s);那它就是“專(zhuān)一”的右值引用。

記?。?/h3>

右值引用&& 是 C++11 的性能優(yōu)化利器,專(zhuān)門(mén)處理“一次性用品”,配合 std::move 實(shí)現(xiàn)高效的資源轉(zhuǎn)移。

萬(wàn)能引用&& 是泛型編程和完美轉(zhuǎn)發(fā)的核心,它像個(gè)變色龍,能適應(yīng)并保持參數(shù)的原始“價(jià)值”,配合 std::forward 確保信息無(wú)損傳遞。

責(zé)任編輯:武曉燕 來(lái)源: CppPlayer
相關(guān)推薦

2019-10-11 07:56:37

物聯(lián)網(wǎng)應(yīng)用物聯(lián)網(wǎng)IOT

2010-02-01 10:22:51

C++數(shù)據(jù)指針

2025-02-10 08:30:00

JavaScrip開(kāi)發(fā)設(shè)計(jì)模式

2024-06-06 08:32:52

.NET框架代碼

2015-04-07 10:46:48

Redis

2024-12-19 08:50:38

Redis存儲(chǔ)系統(tǒng)

2010-01-12 10:57:16

C++的復(fù)雜性

2009-06-29 18:11:40

JSP設(shè)計(jì)模式

2023-11-15 18:40:27

半監(jiān)督學(xué)習(xí)人工智能

2009-08-18 09:22:47

應(yīng)用場(chǎng)景C#分部方法

2009-07-22 15:50:36

J#和C++ASP.NET

2021-04-27 08:31:10

前端應(yīng)用場(chǎng)景

2014-01-07 14:04:13

HadoopMapReduce

2023-03-15 15:58:11

Python動(dòng)態(tài)庫(kù)C++

2020-07-20 14:00:26

架構(gòu)運(yùn)維技術(shù)

2011-02-23 12:49:31

KonquerorEmbedded

2022-02-21 08:18:38

option編程模式

2010-08-26 15:15:18

DB2備份

2024-03-29 08:33:10

應(yīng)用場(chǎng)景存儲(chǔ)搜索

2012-10-23 09:32:07

點(diǎn)贊
收藏

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