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

C++ 右值語義相關(guān)總結(jié)

 更新時(shí)間:2021年02月26日 16:18:38   作者:后端技術(shù)小屋  
這篇文章主要介紹了C++ 右值語義的的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下

在現(xiàn)代C++的眾多特性中,右值語義(std::move和std::forward)大概是最神奇也最難懂的特性之一了。本文簡要介紹了現(xiàn)代C++中右值語義特性的原理和使用。

1 什么是左值,什么是右值?

int a = 0;  // a是左值,0是右值
int b = rand(); // b是左值,rand()是右值

直觀理解:左值在等號(hào)左邊,右值在等號(hào)右邊

深入理解:左值有名稱,可根據(jù)左值獲取其內(nèi)存地址,而右值沒有名稱,不能根據(jù)右值獲取地址。

2 引用疊加規(guī)則

左值引用A&和右值引用A&&可相互疊加, 疊加規(guī)則如下:

A& + A& = A&
A& + A&& = A&
A&& + A& = A&
A&& + A&& = A&&

舉例說明,在模板函數(shù)void foo(T&& x)中:

如果T是int&類型, T&&為int&,x為左值語義
如果T是int&&類型, T&&為int&&, x為右值語義
也就是說,不管輸入?yún)?shù)x為左值還是右值,都能傳入函數(shù)foo。區(qū)別在于兩種情況下,編譯器推導(dǎo)出模板參數(shù)T的類型不一樣。

3 std::move

3.1 What?

在C++11中引入了std::move函數(shù),用于實(shí)現(xiàn)移動(dòng)語義。它用于將臨時(shí)變量(也有可能是左值)的內(nèi)容直接移動(dòng)給被賦值的左值對(duì)象。

3.2 Why?

知道了std::move是干什么的,他能給我們的搬磚工作帶來哪些好處呢? 舉例說明:

如果類X包含一個(gè)指向某資源的指針,在左值語義下,類X的復(fù)制構(gòu)造函數(shù)定義如下:

X::X() 
{
 // 申請(qǐng)資源(指針表示)
}

X::X(const X& other)
{
 // ...
 // 銷毀資源 
 // 克隆other中的資源
 // ...
}

X::~X() 
{
 // 銷毀資源
}

假設(shè)應(yīng)用代碼如下。其中,對(duì)象tmp被賦給a之后,便不再使用。

X tmp;
// ...經(jīng)過一系列初始化...
X a = tmp;

在上面的代碼中,執(zhí)行步驟:

  • 先執(zhí)行一次默認(rèn)構(gòu)造函數(shù)(默認(rèn)構(gòu)造tmp對(duì)象)
  • 再執(zhí)行一次復(fù)制構(gòu)造函數(shù)(復(fù)制構(gòu)造a對(duì)象)
  • 退出作用域時(shí)執(zhí)行析構(gòu)函數(shù)(析構(gòu)tmp和a對(duì)象)

從資源的視角來看,上述代碼中共執(zhí)行了2次資源申請(qǐng)和3次資源釋放。

那么問題來了,既然對(duì)象tmp只是一個(gè)臨時(shí)對(duì)象,在執(zhí)行X a = tmp;時(shí),對(duì)象a能否將tmp的資源'偷'過來,直接為我所用,而不影響原來的功能? 答案是可以。

X::X(const X& other)
{
 // 使用std::swap交換this和other的資源  
}

通過'偷'對(duì)象tmp的資源,減少了資源申請(qǐng)和釋放的開銷。而std::swap交換指針代價(jià)極小,可忽略不計(jì)。

3.3 How?

到現(xiàn)在為止,我們明白了std::move將要達(dá)到的效果,那么它究竟是怎么實(shí)現(xiàn)的呢?

template<class T> 
typename remove_reference<T>::type&&
std::move(T&& a) noexcept
{
 typedef typename remove_reference<T>::type&& RvalRef;
 return static_cast<RvalRef>(a);
}

不管輸入?yún)?shù)為左值還是右值,都被remove_reference去掉其引用屬性,RvalRef為右值類型,最終返回類型為右值引用。

3.4 Example

在實(shí)際使用中,一般將臨時(shí)變量作為std::move的輸入?yún)?shù),并將返回值傳入接受右值類型的函數(shù)中,方便其'偷取'臨時(shí)變量中的資源。需要注意的是,臨時(shí)變量被'偷'了之后,便不能對(duì)其進(jìn)行讀寫,否則會(huì)產(chǎn)生未定義行為。

#include <utility>    
#include <iostream>   
#include <string>    
#include <vector>    
        
void foo(const std::string& n) 
{        
 std::cout << "lvalue" << std::endl;
}        
        
void foo(std::string&& n)  
{        
 std::cout << "rvalue" << std::endl;
}        
        
void bar()      
{        
 foo("hello");    // rvalue
 std::string a = "world";  
 foo(a);      // lvalue
 foo(std::move(a));   // rvalue
}

int main()
{
 std::vector<std::string> a = {"hello", "world"};
 std::vector<std::string> b;

 b.push_back("hello");   // 開銷:string復(fù)制構(gòu)造
 b.push_back(std::move(a[1])); // 開銷:string移動(dòng)構(gòu)造(將臨時(shí)變量a[1]中的指針偷過來)

 std::cout << "bsize: " << b.size() << std::endl;
 for (std::string& x: b)
 std::cout << x << std::endl;
 bar();
 return 0;
}

4 std::forward

4.1 What?

std::forward用于實(shí)現(xiàn)完美轉(zhuǎn)發(fā)。那么什么是完美轉(zhuǎn)發(fā)呢?完美轉(zhuǎn)發(fā)實(shí)現(xiàn)了參數(shù)在傳遞過程中保持其值屬性的功能,即若是左值,則傳遞之后仍然是左值,若是右值,則傳遞之后仍然是右值。

簡單來說,std::move用于將左值或右值對(duì)象強(qiáng)轉(zhuǎn)成右值語義,而std::forward用于保持左值對(duì)象的左值語義和右值對(duì)象的右值語義。

4.2 Why?

#include <utility>
#include <iostream>

void bar(const int& x)
{
 std::cout << "lvalue" << std::endl;
}

void bar(int&& x)
{
 std::cout << "rvalue" << std::endl;
}

template <typename T>
void foo(T&& x)
{
 bar(x);
}

int main()
{
 int x = 10; 
 foo(x); // 輸出:lvalue
 foo(10); // 輸出:lvalue
 return 0;
}

執(zhí)行以上代碼會(huì)發(fā)現(xiàn),foo(x)和foo(10)都會(huì)輸出lvalue。foo(x)輸出lvalue可以理解,因?yàn)閤是左值嘛,但是10是右值,為啥foo(10)也輸出lvalue呢?

這是因?yàn)?0只是作為函數(shù)foo的右值參數(shù),但是在foo內(nèi)部,10被帶入了形參x,而x是一個(gè)有名字的變量,即右值,因此foo中bar(x)還是輸出lvalue。

那么問題來了,如果我們想在foo函數(shù)內(nèi)部保持x的右值語義,該怎么做呢?std::forward便派上了用場。

只需改寫foo函數(shù):

template <typename T>
void foo(T&& x)
{
 bar(std::forward<T>(x));
}

4.3 How?

std::forward聽起來有點(diǎn)神奇,那么它到底是如何實(shí)現(xiàn)的呢?

template<typename T, typename Arg> 
shared_ptr<T> factory(Arg&& arg)
{ 
 return shared_ptr<T>(new T(std::forward<Arg>(arg)));
}
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{
 return static_cast<S&&>(a);
}

X x;
factory<A>(x);

如果factory的輸入?yún)?shù)是一個(gè)左值,那么Arg = X&,根據(jù)疊加規(guī)則,std::forward<Arg> = X&。因此,在這種情況下,std::forward<Arg>(arg)仍然是左值。

相反,如果factory輸入?yún)?shù)是一個(gè)右值,那么Arg = X,std::forward<Arg> = X。這種情況下,std::forward<Arg>(arg)是一個(gè)右值。

恰好達(dá)到了保留左值or右值語義的效果!

4.4 Example

直接上代碼。如果前面都懂了,相信這段代碼的輸出結(jié)果也能猜個(gè)八九不離十了。

#include <utility>
#include <iostream>

void overloaded(const int& x)
{
 std::cout << "[lvalue]" << std::endl;
}

void overloaded(int&& x)
{
 std::cout << "[rvalue]" << std::endl;
}

template <class T> void fn(T&& x)
{
 overloaded(x);
 overloaded(std::forward<T>(x));
}

int main()
{
 int i = 10; 
 overloaded(std::forward<int>(i));
 overloaded(std::forward<int&>(i));
 overloaded(std::forward<int&&>(i));
 
 fn(i);
 fn(std::move(i));

 return 0;
}

以上就是C++ 右值語義相關(guān)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于C++ 右值語義的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

永嘉县| 瑞昌市| 兴安县| 辽宁省| 枝江市| 托克托县| 大悟县| 吴川市| 辽源市| 弥渡县| 马公市| 江安县| 舞阳县| 仪征市| 萝北县| 江安县| 林口县| 广宁县| 玛纳斯县| 米脂县| 南安市| 犍为县| 南宁市| 锡林郭勒盟| 右玉县| 乌什县| 巧家县| 屏东县| 晋城| 菏泽市| 罗平县| 肥乡县| 齐河县| 马公市| 玉林市| 巴彦县| 宝清县| 鹤壁市| 金阳县| 重庆市| 龙门县|