★18.關(guān)于右值引用與完美轉(zhuǎn)發(fā)

右值引用

無名右值引用

  • 無名右值引用是右值。
  • 無名右值引用的產(chǎn)生方式:
// v是具名右值引用,返回值為無名右值引用
int && fun(int && v) {
    return 0;
}
  • std::movestd::forward返回的右值引用為無名右值引用,其原理與上述代碼類似。

具名右值引用

  • 具名右值引用是左值。
  • 具名右值引用只能綁定右值,其定義方式:
// v是具名右值引用,返回值為無名右值引用
int && fun(int && v) {
    return 0;
}

int main() {
    int x1 = 0;
    int & x2 = x1;
    int && x3 = 0;                   // 具名右值引用是左值

    int && y1 = x1;                  // 錯(cuò)誤,無法將右值引用綁定到在左值
    int && y2 = x2;                  // 錯(cuò)誤,無法將右值引用綁定到在左值
    int && y3 = x3;                  // 錯(cuò)誤,無法將右值引用綁定到在左值
    int && y4 = fun(0);              // fun()返回?zé)o名右值引用,是右值
    int && y5 = std::move(0);        // std::move()返回?zé)o名右值引用,是右值
    return system("pause");
}

轉(zhuǎn)發(fā)型引用

描述

  • 轉(zhuǎn)發(fā)型引用可以保留被引用值的const&&&屬性。
  • 轉(zhuǎn)發(fā)型引用可以引用任意值,不論是右值、左值、有無const等。

定義

方式一

// T &&類型參數(shù)t叫轉(zhuǎn)發(fā)型引用
template <typename T>
void fun(T && t) {
}

方式二

const int && x = 0;
auto && xx = x;        // 推斷為const int &,注意x在初始化以后就是const int &類型了

推導(dǎo)規(guī)則

const int x1 = 1;
const int & x2 = x1;
const int && x3 = 1;

int x4 = 1;
int & x5 = x4;
int && x6 = 1;

auto && y1 = x1;                // 推斷為const int &
auto && y2 = x2;                // 推斷為const int &
auto && y3 = x3;                // 推斷為const int &
auto && y4 = x4;                // 推斷為int &
auto && y5 = x5;                // 推斷為int &
auto && y6 = x6;                // 推斷為int &
auto && y7 = 0;                 // 推斷為具名int &&
auto && y8 = std::move(0);      // 推斷為具名int &&

std::move()和std::forward()的區(qū)別

  • std::forward()的必須通過顯式模板實(shí)參來使用。
  • std::move(int)std::forward<int>()std::forward<int &&>()完全無任何區(qū)別,都是轉(zhuǎn)換為無名int &&
  • std::move(int)std::forward<int &>()的區(qū)別是前者依舊轉(zhuǎn)換為int &&,而后者轉(zhuǎn)換為int &
  • 因?yàn)?code>std::forward()的這個(gè)特性,它的通常用法是在模板中使用std::forward<T>(t),并且TT && t的方式捕獲以實(shí)現(xiàn)完美轉(zhuǎn)發(fā)。

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

簡述

完美轉(zhuǎn)發(fā):將類型傳遞給模板參數(shù)時(shí),保留所有實(shí)參類型的細(xì)節(jié)(如const,具名右值引用,無名右值引用、左值引用)。

代碼

#include <iostream>

void f(const int & v) {}    //fun1
// void f(int & v) { }        //fun2
// void f(int && v) { }        //fun3

//不完美轉(zhuǎn)發(fā)版本一
template <typename F, typename T>
void call1(F f, T t) {
    f(t);
}

//不完美轉(zhuǎn)發(fā)版本二
template <typename F, typename T>
void call2(F f, T && t) {
    f(t);
}

//完美轉(zhuǎn)發(fā)
template <typename F, typename T>
void call3(F f, T && t) {
    f(std::forward<T>(t));
}

int main() {
    const int x1 = 1;
    const int & x2 = x1;
    const int && x3 = 1;

    int x4 = 1;
    int & x5 = x4;
    int && x6 = 1;

    call1(f, x1);              //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(可以調(diào)用fun2,但不應(yīng)該可以)
    call1(f, x2);              //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(可以調(diào)用fun2,但不應(yīng)該可以)
    call1(f, x3);              //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(可以調(diào)用fun2,但不應(yīng)該可以)
    call1(f, x4);              //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(傳值的方式調(diào)用fun2,但本應(yīng)該用傳引用的方式)
    call1(f, x5);              //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(傳值的方式調(diào)用fun2,但本應(yīng)該用傳引用的方式)
    call1(f, x6);              //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(傳值的方式調(diào)用fun2,但本應(yīng)該用傳引用的方式)
    call1(f, 0);               //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(不可以調(diào)用fun3,但本應(yīng)該可以)
    call1(f, std::move(0));    //類型參數(shù)推斷為int,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(不可以調(diào)用fun3,但本應(yīng)該可以)

    call2(f, x1);              //類型參數(shù)推斷為const int &,轉(zhuǎn)發(fā)時(shí)類型為const int &,轉(zhuǎn)發(fā)成功。
    call2(f, x2);              //類型參數(shù)推斷為const int &,轉(zhuǎn)發(fā)時(shí)類型為const int &,轉(zhuǎn)發(fā)成功。
    call2(f, x3);              //類型參數(shù)推斷為const int &,轉(zhuǎn)發(fā)時(shí)類型為const int &,轉(zhuǎn)發(fā)成功。
    call2(f, x4);              //類型參數(shù)推斷為int &,轉(zhuǎn)發(fā)時(shí)類型為int &,轉(zhuǎn)發(fā)成功。
    call2(f, x5);              //類型參數(shù)推斷為int &,轉(zhuǎn)發(fā)時(shí)類型為int &,轉(zhuǎn)發(fā)成功。
    call2(f, x6);              //類型參數(shù)推斷為int &,轉(zhuǎn)發(fā)時(shí)類型為int &,轉(zhuǎn)發(fā)成功。
    call2(f, 0);               //類型參數(shù)推斷為具名int &&,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(調(diào)用fun3會(huì)出現(xiàn)無法將右值引用綁定到左值的錯(cuò)誤。)
    call2(f, std::move(0));    //類型參數(shù)推斷為具名int &&,丟失類型細(xì)節(jié)信息,轉(zhuǎn)發(fā)失敗。(調(diào)用fun3會(huì)出現(xiàn)無法將右值引用綁定到左值的錯(cuò)誤。)

    call3(f, x1);              //類型參數(shù)推斷為const int &,轉(zhuǎn)發(fā)時(shí)std::forward將類型const int &轉(zhuǎn)換為const int &,轉(zhuǎn)發(fā)成功。
    call3(f, x2);              //類型參數(shù)推斷為const int &,轉(zhuǎn)發(fā)時(shí)std::forward將類型const int &轉(zhuǎn)換為const int &,轉(zhuǎn)發(fā)成功。
    call3(f, x3);              //類型參數(shù)推斷為const int &,轉(zhuǎn)發(fā)時(shí)std::forward將類型const int &轉(zhuǎn)換為const int &,轉(zhuǎn)發(fā)成功。
    call3(f, x4);              //類型參數(shù)推斷為int &,轉(zhuǎn)發(fā)時(shí)std::forward將類型int &轉(zhuǎn)換為int &,轉(zhuǎn)發(fā)成功。
    call3(f, x5);              //類型參數(shù)推斷為int &,轉(zhuǎn)發(fā)時(shí)std::forward將類型int &轉(zhuǎn)換為int &,轉(zhuǎn)發(fā)成功。
    call3(f, x6);              //類型參數(shù)推斷為int &,轉(zhuǎn)發(fā)時(shí)std::forward將類型int &轉(zhuǎn)換為int &,轉(zhuǎn)發(fā)成功。
    call3(f, 0);               //類型參數(shù)推斷為具名int &&,轉(zhuǎn)發(fā)時(shí)std::forward將具名int &&轉(zhuǎn)換為無名int &&,轉(zhuǎn)發(fā)成功。
    call3(f, std::move(0));    //類型參數(shù)推斷為具名int &&,轉(zhuǎn)發(fā)時(shí)std::forward將具名int &&轉(zhuǎn)換為無名int &&,轉(zhuǎn)發(fā)成功。
    return system("pause");
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容