0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

聊聊std::move函數(shù)和std::forward函數(shù)

CPP開(kāi)發(fā)者 ? 來(lái)源:雨樂(lè)聊編程 ? 2024-11-05 16:58 ? 次閱讀

以下文章來(lái)源于雨樂(lè)聊編程,作者雨樂(lè)

今天我們聊聊Modern cpp的兩個(gè)非常重要的概念移動(dòng)語(yǔ)義轉(zhuǎn)發(fā)引用。

概念

值類(lèi)別

C++11之前,值類(lèi)別分為左值和右值兩種,但是自C++11起,引入了純右值,消亡值兩種。其中,左值和將亡值合稱為泛左值,純右值和將亡值合稱為右值(C++11之前的右值等同于C++11中的純右值)。因?yàn)楸疚哪康牟辉谟诜治鲋殿?lèi)別,所以本文意義中的左值和右值就是字面意義上的左值右值。

右值(RVALUE),即立即創(chuàng)建和使用的臨時(shí)值。在C++中,右值是與內(nèi)存地址無(wú)關(guān)的表達(dá)式,這意味著其沒(méi)有地址,也不能被修改。通常3、1.0以及std::string("abc")這種都屬于右值。

PS:需要注意的是常量字符串"abc"等這種屬于左值。

與右值相反,左值(LVALUE),其具有內(nèi)存地址和可修改,其可以用于分配新值或者獲取對(duì)象的地址。

可能有人有疑問(wèn),就是如何區(qū)分左值和右值,目前一個(gè)比較通用的判斷方式就是:判斷其是否可以取地址。

左值引用 & 右值引用

既然有左值和右值,那么相應(yīng)的,也就存在左值引用右值引用,常常如下這種表示:

inta=0;
int&la=a;
int&&r=3;

在上述示例中,a、la以及r都屬于左值,其中l(wèi)a是左值引用,r是右值引用。

看下面一個(gè)例子:

#include

voidPrint(int&lref){
std::cout<

上述示例輸出如下:

Lvaluereference
constLvaluereference
Rvaluereference

std::move

std::move是C++中的一個(gè)常用函數(shù),它執(zhí)行到右值引用的轉(zhuǎn)換,允許您將左值轉(zhuǎn)換為右值。這在您想要轉(zhuǎn)移所有權(quán)或啟用對(duì)象的移動(dòng)語(yǔ)義的情況下非常有用。移動(dòng)語(yǔ)義允許開(kāi)發(fā)人員有效地將資源(如內(nèi)存或文件句柄)從一個(gè)對(duì)象傳輸?shù)搅硪粋€(gè)對(duì)象,而無(wú)需進(jìn)行不必要的復(fù)制。

正如字面意義所理解的,移動(dòng)語(yǔ)義允許將對(duì)象有效地從一個(gè)位置“移動(dòng)”到另一個(gè)位置,而不是復(fù)制,這對(duì)于管理資源的對(duì)象特別有用。它實(shí)際上并沒(méi)有移動(dòng)任何東西;它只是將表達(dá)式的類(lèi)型更改為右值引用。這允許調(diào)用移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符,而不是調(diào)用復(fù)制構(gòu)造函數(shù)或復(fù)制賦值運(yùn)算符。

gcc對(duì)move的實(shí)現(xiàn)如下:

template
inlinetypenamestd::remove_reference<_Tp>::type&&
move(_Tp&&__t)
{returnstatic_cast::type&&>(__t);}

也就是說(shuō),其僅僅通過(guò)static_cast<>做了類(lèi)型轉(zhuǎn)換~

std::move僅僅將對(duì)象轉(zhuǎn)換為右值引用,僅此而已

#include
#include

classObj{
public:
Obj(){
std::cout<

輸出如下:

Defaultconstructor
Moveconstructor
Defaultconstructor
Moveassignmentoperator

在上述示例中:

?Obj1創(chuàng)建對(duì)象并調(diào)用構(gòu)造函數(shù)?obj2是通過(guò)使用std::move移動(dòng)obj1創(chuàng)建的,它調(diào)用移動(dòng)構(gòu)造函數(shù)?創(chuàng)建obj3并調(diào)用默認(rèn)構(gòu)造函數(shù)?當(dāng)使用std::move將obj2移動(dòng)到 obj3 時(shí),將調(diào)用移動(dòng)賦值運(yùn)算符

在此示例中,使用std::move操作, obj1到obj2 以及 obj2到obj3調(diào)用的是移動(dòng)的行為,這樣可以提高性能,尤其是在移動(dòng)大型數(shù)據(jù)結(jié)構(gòu)或資源時(shí)。但是,重要的是要注意移動(dòng)對(duì)象的狀態(tài)及其擁有的資源。

#include
#include

classObj{
public:
Obj(){
std::cout<p1=std::make_unique();

std::unique_ptrp2=std::move(p1);
if(p1){
std::cout<fun();

return0;
}

在這個(gè)例子中,首先創(chuàng)建了一個(gè)類(lèi)型為std::unique_ptr的指針p1,然后通過(guò)調(diào)用std::move()將p1的所有權(quán)轉(zhuǎn)移至p2,接著判斷p1是否為有效的指針,如果是則輸出,接著p2調(diào)用fun()函數(shù)。

上述示例輸出結(jié)果如下:

Objconstructed
infun
Objdestructed

從這個(gè)輸出結(jié)果可以看出,通過(guò)std::move()將所有權(quán)從p1轉(zhuǎn)移至p2后,p1不再持有任何資源。

std::forward

std::forward是 C++ 標(biāo)準(zhǔn)庫(kù)中的一個(gè)函數(shù)模板,用于在模板函數(shù)中進(jìn)行完美轉(zhuǎn)發(fā)。它允許在模板函數(shù)中將參數(shù)轉(zhuǎn)發(fā)到另一個(gè)函數(shù),同時(shí)保持參數(shù)的值類(lèi)別(value category)和 cv 限定符(const 或 volatile 限定符)不變。

std::forward通常與右值引用(&&)結(jié)合使用,用于轉(zhuǎn)發(fā)傳遞給模板函數(shù)的參數(shù)。在模板函數(shù)內(nèi)部,你可以使用std::forward來(lái)將參數(shù)轉(zhuǎn)發(fā)給其他函數(shù),并保持原始參數(shù)的性質(zhì)。

示例如下:

#include

voidPrint(constint&lref){
std::cout<
voidFun(T&&param){
Print(std::forward(param));
}

intmain(){
intx=5;
constinty=10;

Fun(x);//lvaluereference
Fun(y);//lvaluereference
Fun(20);//rvaluereference

return0;
}

在這個(gè)例子中,我們創(chuàng)建了一個(gè)模板函數(shù)Fun(),其參數(shù)類(lèi)型為T(mén)&&,當(dāng)使用左值調(diào)用Fun()時(shí)候,它將param作為左值進(jìn)行轉(zhuǎn)發(fā),當(dāng)使用右值調(diào)用Fun()時(shí)候,它將param作為右值進(jìn)行轉(zhuǎn)發(fā),然后調(diào)用對(duì)應(yīng)的函數(shù),這樣可保證在不損失真實(shí)類(lèi)型的情況下調(diào)用正確的函數(shù)。

move vs forward

對(duì)于第一次接觸這塊知識(shí)點(diǎn)的開(kāi)發(fā)人員來(lái)說(shuō),可能有點(diǎn)疑惑,是否可以用move來(lái)替代forward,我們且看一個(gè)例子,相信你看了之后就不會(huì)對(duì)這塊一目了然:

#include


voidPrint(int&a){
std::cout<
voidfunc1(T&&a){
Print(std::move(a));
}


template
voidfunc2(T&&a){
Print(std::forward(a));
}

intmain(){
intarg=10;

std::cout<

上述代碼輸出如下:

Callingfunc1withstd::move()...
int&&:10
int&&:25
Callingfunc2withstd::forward()...
int&:10
int&&:25

在上述代碼中:

?創(chuàng)建了兩個(gè)重載函數(shù)Print,其參數(shù)類(lèi)型分別為**int &和int &&**,函數(shù)的功能是輸出其參數(shù)的類(lèi)型

?模板函數(shù)func1(),函數(shù)參數(shù)a為轉(zhuǎn)發(fā)引用(T&&,也有地方稱之為萬(wàn)能引用),函數(shù)體內(nèi)調(diào)用參數(shù)為std::move(a)的Print()函數(shù),將a轉(zhuǎn)換為右值引用,這意味著,如果a是左值,則傳遞給Print()函數(shù)的參數(shù)類(lèi)型為右值引用

?模板函數(shù)func2(),與模板函數(shù)func1()一樣,該函數(shù)也采用轉(zhuǎn)發(fā)引用(T&&)。但是,它使用 std::forward來(lái)保留a的原始值類(lèi)別。這意味著如果a是左值,它將作為左值傳遞給Print()函數(shù),如果它是右值,它將作為右值傳遞

?在 main() 中,使用左值和右值調(diào)用函數(shù)func1和func2,以觀察對(duì)應(yīng)的行為

通過(guò)上面輸出,基本可以區(qū)分這倆,在此,做下簡(jiǎn)單的總結(jié):

?目的

?std::forward:用于完全按照傳遞的參數(shù)轉(zhuǎn)發(fā),保留其值類(lèi)別(左值或右值)

?std::move:用于將對(duì)象轉(zhuǎn)換為右值引用,通常用于啟用移動(dòng)語(yǔ)義并轉(zhuǎn)移所有權(quán)

?用法

?std::forward:通常用于轉(zhuǎn)發(fā)引用(通用引用),以保留傳遞給另一個(gè)函數(shù)的參數(shù)的值類(lèi)別

?std::move:用于將對(duì)象顯式轉(zhuǎn)換為右值引用

?影響

?std::forward:不更改參數(shù)的值類(lèi)別。如果原始參數(shù)是右值引用,則它返回右值引用,否則返回左值引用

?std::move:將其參數(shù)轉(zhuǎn)換為右值引用,將其值類(lèi)別更改為右值

?安全

?std::forward:可以安全地與轉(zhuǎn)發(fā)引用 (T&&) 一起使用,以確保正確轉(zhuǎn)發(fā)參數(shù),而不會(huì)產(chǎn)生不必要的副本。

?std::move:應(yīng)謹(jǐn)慎使用,因?yàn)樗赡軙?huì)導(dǎo)致從其他地方仍需要的對(duì)象移動(dòng),從而導(dǎo)致未定義的行為

?場(chǎng)景

?std::forward:用于需要完美轉(zhuǎn)發(fā)參數(shù)的場(chǎng)景,例如模板函數(shù)和類(lèi)中。

?std::move:在顯式轉(zhuǎn)移所有權(quán)或調(diào)用移動(dòng)語(yǔ)義時(shí)使用,例如從函數(shù)返回僅移動(dòng)類(lèi)型時(shí)

?返回類(lèi)型

?std::forward:返回類(lèi)型取決于傳遞給它的參數(shù)的值類(lèi)別,它可以返回左值引用或右值引用。

?std::move:始終返回右值引用

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    3025

    瀏覽量

    74042
  • 字符串
    +關(guān)注

    關(guān)注

    1

    文章

    579

    瀏覽量

    20514
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4331

    瀏覽量

    62609
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2108

    瀏覽量

    73646

原文標(biāo)題:性能大殺器:std::move 和 std::forward

文章出處:【微信號(hào):CPP開(kāi)發(fā)者,微信公眾號(hào):CPP開(kāi)發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    SAC-STD標(biāo)準(zhǔn)

    本帖最后由 eehome 于 2013-1-5 09:48 編輯 那位工程師有關(guān)于SAC-STD-001 Certification StandardSAC-STD-002 Audit
    發(fā)表于 11-17 18:57

    std::inner_product函數(shù)運(yùn)算特別緩慢如何優(yōu)化?

    ();for(,,,)std::inner_product(samples);i2s_write(samples);結(jié)果就是,inner_product占用了大量的時(shí)間,導(dǎo)致音頻沒(méi)有辦法及時(shí)的處理,在esp32 Lyra-T
    發(fā)表于 02-15 08:11

    如何使用std::future/std::promise 和 std::packaged_task來(lái)促進(jìn)esp32上的多線程?

    我正在考慮使用 std::future/std::promise 和 std::packaged_task 來(lái)促進(jìn) esp32 上的多線程。我知道多任務(wù)函數(shù)之間的抽象分層如下:
    發(fā)表于 03-02 08:44

    IEEE Std C37.09-1999標(biāo)準(zhǔn)

    IEEE Std C37.09™-1999(Revision ofIEEE Std C37.09-1979) Errata toIEEE Standard Test Procedure
    發(fā)表于 07-24 22:18 ?12次下載

    IEEE Std 802.1ad-2005標(biāo)準(zhǔn)

    IEEE Std 802.1ad™-2005(Amendment toIEEE Std 802.1QTM-2005)IEEE Standard forLocal
    發(fā)表于 07-27 18:20 ?62次下載

    IEEE Std C57.12.37™-2006

    IEEE Std C57.12.37™-2006(Revision ofIEEE Std 1388™-2000)IEEE Standard
    發(fā)表于 07-27 18:28 ?16次下載

    IEEE STD C57.12.59-2001標(biāo)準(zhǔn)

    IEEE STD C57.12.59-2001標(biāo)準(zhǔn),IEEE STD C57.12.59-2001標(biāo)準(zhǔn)
    發(fā)表于 07-27 18:32 ?27次下載

    IEEE Std C57.12.90™-2006

    IEEE Std C57.12.90TM-2006(Revision ofIEEE Std C57.12.90-1999)IEEE Standard Test Codefor
    發(fā)表于 07-27 18:33 ?54次下載

    IEEE Std 1226-1998

    IEEE Std 1226-1998(Revision ofIEEE Std 1226-1993)IEEE ABBET™IEEE Trial-Use Standard for A
    發(fā)表于 08-11 23:46 ?26次下載

    IEEE Std C37.081a-1997

    IEEE Std C37.081a-1997(Supplement to IEEE Std C37.081-1981)Supplement to IEEE Guide
    發(fā)表于 08-11 23:50 ?21次下載

    一篇“非典型”面試經(jīng)驗(yàn)貼在Reddit上引發(fā)熱議

    你的代碼中:包括模板、繼承、指針,引用,std :: vector,std :: unordered_map,std :: movestd
    的頭像 發(fā)表于 04-30 08:48 ?2704次閱讀

    std::function簡(jiǎn)介及模板類(lèi)聲明

    01 — std::function簡(jiǎn)介 std::function是一個(gè)函數(shù)包裝器,該函數(shù)包裝器模板能包裝任何類(lèi)型的可調(diào)用實(shí)體,如普通函數(shù)
    的頭像 發(fā)表于 07-28 15:30 ?4823次閱讀

    在C++中如何用虛函數(shù)實(shí)現(xiàn)多態(tài)

    。 // virtual_function.cpp : 此文件包含 “main” 函數(shù)。程序執(zhí)行將在此處開(kāi)始并結(jié)束。 // #include 《iostream》 class Base { public: Base() { std
    的頭像 發(fā)表于 09-29 14:18 ?1699次閱讀

    C++ std::tie函數(shù)的作用和用法

    C++中std::tie函數(shù)的作用就是從元素引用中生成一個(gè)tuple元組,其在頭文件中定義
    的頭像 發(fā)表于 07-18 17:28 ?852次閱讀

    動(dòng)態(tài)數(shù)組和C++ std::vector詳解

    std::vector是C++的默認(rèn)動(dòng)態(tài)數(shù)組,其與array最大的區(qū)別在于vector的數(shù)組是動(dòng)態(tài)的,即其大小可以在運(yùn)行時(shí)更改。std::vector是封裝動(dòng)態(tài)數(shù)組的順序容器,且該容器中元素的存取是連續(xù)的。
    的頭像 發(fā)表于 07-19 11:07 ?974次閱讀