欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

直觀理解:左值在等號左邊,右值在等號右邊

深入理解:左值有名稱,可根據(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ù),用于實現(xiàn)移動語義。它用于將臨時變量(也有可能是左值)的內(nèi)容直接移動給被賦值的左值對象。

3.2 Why?

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

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

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

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

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

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

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

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

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

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

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

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

通過'偷'對象tmp的資源,減少了資源申請和釋放的開銷。而std::swap交換指針代價極小,可忽略不計。

3.3 How?

到現(xiàn)在為止,我們明白了std::move將要達到的效果,那么它究竟是怎么實現(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

在實際使用中,一般將臨時變量作為std::move的輸入?yún)?shù),并將返回值傳入接受右值類型的函數(shù)中,方便其'偷取'臨時變量中的資源。需要注意的是,臨時變量被'偷'了之后,便不能對其進行讀寫,否則會產(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移動構(gòu)造(將臨時變量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用于實現(xiàn)完美轉(zhuǎn)發(fā)。那么什么是完美轉(zhuǎn)發(fā)呢?完美轉(zhuǎn)發(fā)實現(xiàn)了參數(shù)在傳遞過程中保持其值屬性的功能,即若是左值,則傳遞之后仍然是左值,若是右值,則傳遞之后仍然是右值。

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

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í)行以上代碼會發(fā)現(xiàn),foo(x)和foo(10)都會輸出lvalue。foo(x)輸出lvalue可以理解,因為x是左值嘛,但是10是右值,為啥foo(10)也輸出lvalue呢?

這是因為10只是作為函數(shù)foo的右值參數(shù),但是在foo內(nèi)部,10被帶入了形參x,而x是一個有名字的變量,即右值,因此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聽起來有點神奇,那么它到底是如何實現(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ù)是一個左值,那么Arg = X&,根據(jù)疊加規(guī)則,std::forward<Arg> = X&。因此,在這種情況下,std::forward<Arg>(arg)仍然是左值。

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

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

4.4 Example

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

#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é)的詳細內(nèi)容,更多關(guān)于C++ 右值語義的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言代碼實現(xiàn)簡單掃雷游戲

    C語言代碼實現(xiàn)簡單掃雷游戲

    這篇文章主要為大家詳細介紹了C語言代碼實現(xiàn)簡單掃雷游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • C++中的ilst使用以及模擬實現(xiàn)

    C++中的ilst使用以及模擬實現(xiàn)

    list是一個類模板,加<類型>實例化才是具體的類,可以在任意位置進行插入和刪除的序列式容器,本文將通過代碼示例給大家介紹一下C++中的ilst使用以及模擬實現(xiàn),需要的朋友可以參考下
    2023-08-08
  • 一起來學習C++的函數(shù)指針和函數(shù)對象

    一起來學習C++的函數(shù)指針和函數(shù)對象

    這篇文章主要為大家詳細介紹了C++函數(shù)指針和函數(shù)對象,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C/C++實現(xiàn)磁盤相關(guān)操作的示例代碼

    C/C++實現(xiàn)磁盤相關(guān)操作的示例代碼

    這篇文章主要為大家詳細介紹了C/C++如何實現(xiàn)磁盤相關(guān)操作,例如遍歷磁盤容量、實現(xiàn)磁盤格式化、移除指定磁盤等,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-11-11
  • C++?TCP網(wǎng)絡(luò)編程詳細講解

    C++?TCP網(wǎng)絡(luò)編程詳細講解

    TCP/IP是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,它會保證數(shù)據(jù)不丟包、不亂序。TCP全名是Transmission?Control?Protocol,它是位于網(wǎng)絡(luò)OSI模型中的第四層
    2022-09-09
  • C++學習之Lambda表達式的用法詳解

    C++學習之Lambda表達式的用法詳解

    Lambda?表達式(lambda?expression)是一個匿名函數(shù),Lambda表達式基于數(shù)學中的λ演算得名。本文就來為大家詳細講講C++中Lambda表達式的使用,需要的可以參考一下
    2022-07-07
  • C++中常量與指針的示例詳解

    C++中常量與指針的示例詳解

    在C++學習使用過程中,每個人都不可避免地使用指針,而且都或多或少的接觸過常量指針或指針常量,但是對這兩個的概念還是很容易搞糊涂的,所以這篇文章主要給大家介紹了關(guān)于C++中常量與指針的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • VC程序設(shè)計中CreateProcess用法注意事項

    VC程序設(shè)計中CreateProcess用法注意事項

    這篇文章主要介紹了VC程序設(shè)計中CreateProcess用法注意事項,需要的朋友可以參考下
    2014-07-07
  • C++利用數(shù)組(一維/二維)處理批量數(shù)據(jù)的方法

    C++利用數(shù)組(一維/二維)處理批量數(shù)據(jù)的方法

    對于簡單的問題,使用簡單的數(shù)據(jù)類型就可以了,但是對于有些需要處理的數(shù)據(jù),只用以上簡單的數(shù)據(jù)類型是不夠的,難以反映出數(shù)據(jù)的特點,也難以有效的進行處理,本文小編給大家介紹了C++利用數(shù)組(一維/二維)處理批量數(shù)據(jù)的方法,需要的朋友可以參考下
    2023-10-10
  • C語言學習之柔性數(shù)組詳解

    C語言學習之柔性數(shù)組詳解

    結(jié)構(gòu)體的最后一個元素允許是未知大小的數(shù)組,這就叫柔性數(shù)組。這篇文中主要為大家詳細介紹了C語言中柔性數(shù)組的相關(guān)知識,需要的可以了解一下
    2023-03-03

最新評論