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

詳解C++右值引用

 更新時(shí)間:2021年06月07日 09:43:28   作者:可可西  
很多初學(xué)者都感覺(jué)右值引用晦澀難懂,其實(shí)不然。右值引用只不過(guò)是一種新的 C++ 語(yǔ)法,真正理解起來(lái)有難度的是基于右值引用引申出的2種 C++ 編程技巧,分別為移動(dòng)語(yǔ)義和完美轉(zhuǎn)發(fā)。本節(jié)給讀者講解什么是右值引用以及它的基本用法。

概述

在C++中,常量、變量或表達(dá)式一定是左值(lvalue)或右值(rvalue)。

左值:非臨時(shí)的(具名的,可在多條語(yǔ)句中使用,可以被取地址)??梢猿霈F(xiàn)在等號(hào)的左邊或右邊??煞譃榉浅A孔笾岛统A孔笾?。

右值:臨時(shí)的(不具名的,只在當(dāng)前語(yǔ)句中有效,不能取地址)。只能出現(xiàn)在等號(hào)的右邊??煞譃榉浅A坑抑岛统A坑抑怠?/p>

左值引用:對(duì)左值的引用就是左值引用。可分為非常量左值引用和常量左值引用。

注:常量左值引用是“萬(wàn)能”的引用類(lèi)型,可以綁定到所有類(lèi)型的值,包括非常量左值、常量左值、非常量右值和常量右值。

右值引用(Rvalue References):對(duì)右值的引用就是右值引用??煞譃榉浅A坑抑狄煤统A坑抑狄?。

為臨時(shí)對(duì)象的右值,它的生命周期很短暫,一般在執(zhí)行完當(dāng)前這條表達(dá)式之后,就釋放了。

通過(guò)將其賦值給右值引用,可以在不進(jìn)行昂貴的拷貝操作的情況下被“續(xù)命”,讓其生命周期與右值引用類(lèi)型變量的生命周期一樣長(zhǎng)。

右值引用的兩個(gè)基本特性:移動(dòng)語(yǔ)義(Move Semantics)和完美轉(zhuǎn)發(fā)(Perfect Forwarding)

移動(dòng)語(yǔ)義(Move Semantics)

可將資源從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象;主要解決減少不必要的臨時(shí)對(duì)象的創(chuàng)建、拷貝與銷(xiāo)毀。

移動(dòng)構(gòu)造函數(shù)MyClass(Type&& a):當(dāng)構(gòu)造函數(shù)參數(shù)是一個(gè)右值時(shí),優(yōu)先使用移動(dòng)構(gòu)造函數(shù)而不是拷貝構(gòu)造函數(shù)MyClass(const Type& a)。

移動(dòng)賦值運(yùn)算符Type& operator = (Type&& a):當(dāng)賦值的是一個(gè)右值時(shí),優(yōu)先使用移動(dòng)賦值而不是拷貝賦值運(yùn)算符Type& operator = (const Type& a)。

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

struct MyClass
{
    std::string s;
    MyClass(const char* sz) : s(sz) 
    {
        std::cout << "MyClass sz:" << sz << std::endl;
    }
    MyClass(const MyClass& o) : s(o.s) 
    { 
        std::cout << "copy construct!\n"; 
    }

    MyClass(MyClass&& o) noexcept : s(std::move(o.s)) 
    {
        std::cout << "move construct!\n";
    }

    MyClass& operator=(const MyClass& other) { // copy assign
        std::cout << "copy assign!\n";
        s = other.s;
        return *this;
    }
    MyClass& operator=(MyClass&& other) noexcept { // move assign
        std::cout << "move assign!\n";
        s = std::move(other.s);
        return *this;
    }

    static MyClass GetMyClassGo(const char* sz)
    {
        MyClass o(sz); // 注意:可能會(huì)被NRVO優(yōu)化掉
        return o;
    }
};

void func0(MyClass o)
{
    std::cout << o.s.c_str() << std::endl;
}

void func1(MyClass& o)
{
    std::cout << o.s.c_str() << std::endl;
}

void func2(const MyClass& o)
{
    std::cout << o.s.c_str() << std::endl;
}

void func3(MyClass&& o)
{
    std::cout << o.s.c_str() << std::endl;
}

int main(int arg, char* argv[])
{
    MyClass a1("how");
    MyClass a2("are");

    a2 = a1; // copy assign  注:a1是一個(gè)左值

    a2 = MyClass("you"); // move assign  注:MyClass("you")是一個(gè)右值

    MyClass a3(a1); // copy construct  注:a1是一個(gè)左值
    MyClass&& a4 = MyClass::GetMyClassGo("go"); // move construct  注:發(fā)生在MyClass::GetMyClassGo()內(nèi)部
    MyClass a5 = MyClass::GetMyClassGo("china"); // move construct兩次  注:一次發(fā)生在MyClass::GetMyClassGo()內(nèi)部;另一次發(fā)生在將返回值賦值給a5
    
    MyClass a6("let");
    MyClass a7("it");
    MyClass a8("go");
    MyClass a9("!");

    func0(a6);  // copy construct
    func1(a7);
    func2(a8);
    //func3(a9); // 編譯error: 不能把一個(gè)左值賦值給右值

    func0(MyClass::GetMyClassGo("god")); // move construct兩次  注:一次發(fā)生在MyClass::GetMyClassGo()內(nèi)部;另一次發(fā)生在將返回值賦值給foo0參數(shù)時(shí)
    //func1(MyClass::GetMyClassGo("is")); // 編譯error: 不能把一個(gè)右值賦值給左值
    func2(MyClass::GetMyClassGo("girl")); // move construct  注:發(fā)生在MyClass::GetMyClassGo()內(nèi)部
    func3(MyClass::GetMyClassGo("!"));  // move construct  注:發(fā)生在MyClass::GetMyClassGo()內(nèi)部

    return 0;
}

注:測(cè)試以上代碼一定要關(guān)閉C++編譯器優(yōu)化技術(shù) -- RVO、NRVO和復(fù)制省略

使用std::move來(lái)實(shí)現(xiàn)移動(dòng)語(yǔ)義

將一個(gè)左值或右值強(qiáng)制轉(zhuǎn)化為右值引用。 注:UE4中對(duì)應(yīng)為MoveTemp模板函數(shù)

std::move(en chs)并不會(huì)移動(dòng)任何東西,只是將對(duì)象的狀態(tài)或者所有權(quán)從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象。注:只是轉(zhuǎn)移,沒(méi)有內(nèi)存的搬遷或者內(nèi)存拷貝。

① 基本類(lèi)型(如:int、double等)被std::move移動(dòng)后,其數(shù)值不會(huì)發(fā)生變化

② 復(fù)合類(lèi)型被std::move移動(dòng)后,處于一個(gè)未定義,但有效的狀態(tài)(大部分成員函數(shù)仍有意義)例如:標(biāo)準(zhǔn)庫(kù)中的容器類(lèi)對(duì)象被移動(dòng)后,會(huì)變成空容器

完美轉(zhuǎn)發(fā)(Perfect Forwarding)

針對(duì)模板函數(shù),使用全能引用將一組參數(shù)原封不動(dòng)的傳遞給另一個(gè)函數(shù)。

原封不動(dòng)指:左值、右值、是否為const均不變。帶來(lái)如下3方面好處:

① 保證左值、右值的屬性

② 避免不必要的拷貝操作

③避免模版函數(shù)需要為左值、右值、是否為const的參數(shù)來(lái)實(shí)現(xiàn)不同的重載

全能引用(universal references、轉(zhuǎn)發(fā)引用)是一種特殊的模板引用類(lèi)型,采用右值引用的語(yǔ)法形式(但它并不是右值引用)。如:template <class T> void func(T&& t) {}

T&& t在發(fā)生自動(dòng)類(lèi)型推斷的時(shí)候,它是未定的引用類(lèi)型(universal references),T取決于傳入的參數(shù)t是右值還是左值。右值經(jīng)過(guò)T&&變?yōu)橛抑狄?,而左值?jīng)過(guò)T&&變?yōu)樽笾狄谩?/p>

std::move就是使用全能引用實(shí)現(xiàn)的。其定義如下:

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
    return static_cast<typename remove_reference<T>::type &&>(t);
}


/*****************************************
std::remove_reference功能為去除類(lèi)型中的引用

std::remove_reference<T &>::type ---> T
std::remove_reference<T &&>::type ---> T
std::remove_reference<T>::type ---> T
******************************************/
//原始的,最通用的版本
template <typename T> struct remove_reference{
    typedef T type;  //定義T的類(lèi)型別名為type
};
 
//部分版本特例化,將用于左值引用和右值引用
template <class T> struct remove_reference<T&> //左值引用
{ typedef T type; }
 
template <class T> struct remove_reference<T&&> //右值引用
{ typedef T type; }

① 當(dāng)t為左值時(shí),展開(kāi)為:U&& move(U& t) 注:右值引用類(lèi)型變量也是左值

② 當(dāng)t為右值時(shí),展開(kāi)為:U&& move(U&& t)

最后,通過(guò)static_cast<>進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換返回右值引用。注:static_cast之所以能使用類(lèi)型轉(zhuǎn)換,是通過(guò)remove_refrence::type模板移除T&&,T&的引用,獲取具體類(lèi)型T(模板偏特化)。

引用折疊

規(guī)律:含左值引用就是左值引用,否則就是右值引用

使用std::forward實(shí)現(xiàn)參數(shù)的完美轉(zhuǎn)發(fā)。其定義如下(en chs):

template <typename T>
T&& forward(remove_reference_t<T>& arg) // forward an lvalue as either an lvalue or an rvalue
{ 
    return static_cast<T&&>(arg);
}

template <typename T>
T&& forward(remove_reference_t<T>&& arg) // forward an rvalue as an rvalue
{ 
    static_assert(!is_lvalue_reference_v<T>, "bad forward call");
    return static_cast<T&&>(arg);
}

最后,通過(guò)static_cast<>進(jìn)行引用折疊,并強(qiáng)制類(lèi)型轉(zhuǎn)換后,實(shí)現(xiàn)原封不動(dòng)轉(zhuǎn)發(fā)參數(shù)。 注:UE4中對(duì)應(yīng)為Forward模板函數(shù)

void bar(int& a, int&& b)
{
    int c = a + b;
}

void func(int a, int&& b)
{
    int c = a + b;
}

template <typename A, typename B>
void foo(A&& a, B&& b) { // a, b為左值引用或右值引用
    bar(std::forward<A>(a), std::forward<B>(b)); // 在std::forward轉(zhuǎn)發(fā)前后,參數(shù)a,b的類(lèi)型完全不變
}

int main(int arg, char* argv[])
{
    int a = 10;

    foo(a, 20); // 展開(kāi)為void foo(int& a, int&& b),經(jīng)過(guò)std::forward完美轉(zhuǎn)發(fā)后,會(huì)調(diào)用到void bar(int& a, int&& b)函數(shù)

    func(std::forward<int>(a), std::forward<int&&>(30)); // 經(jīng)過(guò)std::forward完美轉(zhuǎn)發(fā)后,會(huì)調(diào)用到void func(int a, int&& b)函數(shù)

    return 0;
}

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

相關(guān)文章

  • Opencv實(shí)現(xiàn)最小外接矩形和圓

    Opencv實(shí)現(xiàn)最小外接矩形和圓

    這篇文章主要為大家詳細(xì)介紹了Opencv實(shí)現(xiàn)最小外接矩形和圓,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • C++通過(guò)自定義函數(shù)求一元二次方程的根

    C++通過(guò)自定義函數(shù)求一元二次方程的根

    這篇文章主要介紹了C++通過(guò)自定義函數(shù)求一元二次方程的根,涉及C++數(shù)學(xué)運(yùn)算相關(guān)技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2016-05-05
  • Matlab繪制雨云圖的方法詳解

    Matlab繪制雨云圖的方法詳解

    這篇文章主要介紹了如何利用Matlab實(shí)現(xiàn)雨云圖的繪制,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定的幫助,需要的可以參考一下
    2022-05-05
  • c語(yǔ)言如何設(shè)置隨機(jī)數(shù)及逐行解析

    c語(yǔ)言如何設(shè)置隨機(jī)數(shù)及逐行解析

    在C語(yǔ)言中,rand()函數(shù)可以用來(lái)產(chǎn)生隨機(jī)數(shù),但是這不是真真意義上的隨機(jī)數(shù),是一個(gè)偽隨機(jī)數(shù),下面這篇文章主要給大家介紹了關(guān)于c語(yǔ)言如何設(shè)置隨機(jī)數(shù)及逐行解析的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • C語(yǔ)言的strcpy函數(shù)你了解嗎

    C語(yǔ)言的strcpy函數(shù)你了解嗎

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言的strcpy函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • QT實(shí)現(xiàn)按鈕開(kāi)關(guān)Form窗體的效果的示例代碼

    QT實(shí)現(xiàn)按鈕開(kāi)關(guān)Form窗體的效果的示例代碼

    本文主要介紹了QT實(shí)現(xiàn)按鈕開(kāi)關(guān)Form窗體的效果的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • C++設(shè)計(jì)模式之享元模式

    C++設(shè)計(jì)模式之享元模式

    這篇文章主要介紹了C++設(shè)計(jì)模式之享元模式,本文講解了什么是享元模式、享元模式代碼實(shí)例、享元模式的優(yōu)點(diǎn)等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • C語(yǔ)言深入探究直接插入排序與希爾排序使用案例講解

    C語(yǔ)言深入探究直接插入排序與希爾排序使用案例講解

    算法中排序是十分重要的,而每一個(gè)學(xué)習(xí)計(jì)算機(jī)的都會(huì)在初期的時(shí)候接觸到這種排序,下面這篇文章主要給大家介紹了關(guān)于c語(yǔ)言直接插入排序與希爾排序使用的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • C語(yǔ)言實(shí)現(xiàn)俄羅斯方塊源代碼

    C語(yǔ)言實(shí)現(xiàn)俄羅斯方塊源代碼

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)俄羅斯方塊的源代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • 使用C語(yǔ)言實(shí)現(xiàn)最小生成樹(shù)求解的簡(jiǎn)單方法

    使用C語(yǔ)言實(shí)現(xiàn)最小生成樹(shù)求解的簡(jiǎn)單方法

    這篇文章主要介紹了使用C語(yǔ)言實(shí)現(xiàn)最小生成樹(shù)求解的簡(jiǎn)單方法,包括Prim算法和Kruskal算法的兩種求解方式,需要的朋友可以參考下
    2015-08-08

最新評(píng)論