精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

網(wǎng)易實習(xí)C++三面:std::move 與 std::forward 的區(qū)別

開發(fā) 前端
左值,英文名叫 “l(fā)value” ,其實可以理解為有明確存儲位置且可以被取地址的表達式 。簡單來說,一個可以出現(xiàn)在賦值符號左邊的對象通常就是左值。

在C++編程領(lǐng)域,高效利用資源與精準(zhǔn)控制對象生命周期是進階路上的關(guān)鍵挑戰(zhàn)。當(dāng)開發(fā)者著手處理復(fù)雜數(shù)據(jù)結(jié)構(gòu)和頻繁對象操作時,兩個看似不起眼卻極為強大的工具 ——std::move與std::forward,便登上了優(yōu)化舞臺的中央。它們雖同屬 C++11 引入的革新特性,功能卻大相徑庭,各自在特定場景中發(fā)揮著無可替代的作用。

std::move如同一位大膽的資源搬運工,勇于將對象資源進行轉(zhuǎn)移;std::forward則像謹(jǐn)慎的信息傳遞員,確保參數(shù)在模板函數(shù)的復(fù)雜傳遞過程中,始終保持其原本的左值或右值特性。左值(lvalue)和右值(rvalue)是兩個極為重要的概念。理解它們,就像是掌握了開啟 C++ 高效編程大門的鑰匙,而std::move和std::forward這兩個工具,也與左值和右值緊密相關(guān)。那么,到底什么是左值和右值呢?

一、左值:穩(wěn)固的內(nèi)存 “定居者”

左值,英文名叫 “l(fā)value” ,其實可以理解為有明確存儲位置且可以被取地址的表達式 。簡單來說,一個可以出現(xiàn)在賦值符號左邊的對象通常就是左值。就好比在游戲里,左值是有自己固定 “住址” 的角色,這個 “住址” 就是內(nèi)存位置,我們可以通過地址找到它,還能隨時修改它的狀態(tài)。

比如在 C++ 中,我們定義一個變量int num = 10;,這里的num就是左值。它在內(nèi)存中有自己的 “小窩”,我們可以通過&num獲取它的地址,就像知道了它家的門牌號 。而且,我們還能給它重新賦值,比如num = 20; ,改變它存儲的值,這就好比給這個 “小窩” 換了新的裝飾。

左值有三個非常重要的特性,分別是持久性、可尋址性和可修改性。持久性就是說左值在程序運行過程中一直存在,除非它所在的作用域結(jié)束。就像一個長期定居的居民,只要這個小區(qū)(作用域)不拆,它就一直住在這兒??蓪ぶ沸詣偛乓呀?jīng)提到,就是可以通過取地址符&獲取它在內(nèi)存中的地址,知道它具體住在哪間房。可修改性則是能對左值存儲的值進行修改,就像可以改變房子里的布置一樣。

在實際編程中,左值的應(yīng)用場景非常多。在變量賦值時,左值是賦值的目標(biāo),比如int a; a = 5; ,這里的a就是左值,把5這個值賦給它。在函數(shù)參數(shù)傳遞中,如果參數(shù)是左值引用,那么可以通過這個引用修改傳入的左值,比如:

void modifyValue(int& value) {
    value = value * 2;
}
int main() {
    int num = 10;
    modifyValue(num);
    // 此時num的值變?yōu)?0
    return 0;
}

在各類運算操作里,左值也常作為操作數(shù)出現(xiàn)。例如int result = num + 5;這句代碼中,num就是以左值的身份參與了加法運算。

二、右值:短暫的內(nèi)存 “過客”

了解完左值這位內(nèi)存 “定居者”,我們再來認(rèn)識一下右值這位內(nèi)存 “過客”。右值,英文是 “rvalue” ,和左值相反,它是指那些沒有明確存儲位置或者不能被取地址的表達式 。就像游戲里突然出現(xiàn)又消失的道具,它沒有固定的 “住址”,只是在需要的時候短暫出現(xiàn),完成任務(wù)后就消失不見。

在 C++ 中,字面常量就是典型的右值,比如10、3.14、"hello"等 。它們就像是游戲里的一次性道具,用完就沒了,你沒辦法找到它們的固定位置。還有臨時對象,比如函數(shù)的返回值(如果是一個臨時對象)、表達式的計算結(jié)果等也是右值。例如:

int add(int a, int b) {
    return a + b;
}
int main() {
    int result = add(3, 5); 
    int temp = 3 + 5; 
    return 0;
}

在這個例子中,add(3, 5)的返回值8是右值,它是一個臨時結(jié)果,沒有自己獨立的內(nèi)存地址可以被獲取 。3 + 5這個表達式的計算結(jié)果8同樣也是右值 。

右值有三個特性,分別是臨時性、不可尋址性和不可直接賦值性。臨時性是說右值只在表達式計算期間存在,一旦表達式結(jié)束,右值就消失了,就像游戲里限時出現(xiàn)的道具。不可尋址性剛才已經(jīng)提到,就是不能通過取地址符&獲取它的地址,因為它根本沒有固定地址。不可直接賦值性是指右值不能出現(xiàn)在賦值符號的左邊,不能被直接賦值,因為它沒有固定的存儲位置來接收值 。

右值在實際編程中,主要用于初始化左值或者作為臨時的值參與運算。比如我們前面提到的int result = add(3, 5); ,這里就是用右值add(3, 5)的返回值來初始化左值result 。在運算中,int temp = 3 + 5; ,3 + 5這個右值參與了賦值運算 。

三、左右相逢:引用的魔法

白了左值與右值的概念后,我們再來看看它們和引用之間的聯(lián)系。在 C++ 里,引用好比是對象的 “別名”,借助引用能夠直接對對象進行操作。其中,左值引用和右值引用又分別與左值、右值存在著緊密的對應(yīng)關(guān)系。

3.1左值引用:對象的貼心 “別名”

左值引用,簡單來說,就是給左值取一個別名。它的語法是type &引用名 = 左值表達式; ,這里的type是數(shù)據(jù)類型,比如int、double等 。通過左值引用,我們可以直接操作原來的左值對象,就像給這個對象取了一個親切的小名,叫小名和叫大名都是在叫同一個對象。

左值引用的主要作用有三個。第一個是避免對象拷貝,當(dāng)我們需要將一個對象傳遞給函數(shù)時,如果直接傳遞對象,會進行對象的拷貝,這在對象比較大時會消耗大量的時間和內(nèi)存。而使用左值引用傳遞,就不會進行拷貝,直接操作原對象,大大提高了效率。比如下面這個函數(shù):

void printLength(const std::string& str) {
    std::cout << "Length of the string: " << str.length() << std::endl;
}
int main() {
    std::string name = "Alice";
    printLength(name); 
    return 0;
}

在這個例子中,printLength函數(shù)的參數(shù)str是左值引用,這樣在調(diào)用函數(shù)時,不會對name進行拷貝,直接使用name對象 。

第二個作用是讓函數(shù)可以直接修改傳遞進來的參數(shù)。還是上面那個例子,如果我們把函數(shù)改為:

void appendString(std::string& str) {
    str += " Wonderland";
}
int main() {
    std::string name = "Alice";
    appendString(name);
    std::cout << name << std::endl; 
    return 0;
}

這里appendString函數(shù)的參數(shù)str也是左值引用,在函數(shù)內(nèi)部對str的修改會直接影響到name,因為它們是同一個對象 。

第三個作用是實現(xiàn)一些高效的操作,比如鏈?zhǔn)秸{(diào)用。有些類的成員函數(shù)返回左值引用,這樣就可以連續(xù)調(diào)用多個成員函數(shù),就像一條鏈子一樣。例如:

class MyClass {
public:
    MyClass& setValue(int value) {
        m_value = value;
        return *this;
    }
    void printValue() {
        std::cout << "Value: " << m_value << std::endl;
    }
private:
    int m_value;
};
int main() {
    MyClass obj;
    obj.setValue(10).printValue(); 
    return 0;
}

在這個例子中,setValue函數(shù)返回*this,也就是當(dāng)前對象的左值引用,這樣就可以接著調(diào)用printValue函數(shù) 。

3.2右值引用:資源的高效 “搬運工”

右值引用,是 C++11 引入的一個新特性,它的語法是type &&引用名 = 右值表達式; ,專門用來給右值取別名 。右值引用就像是一個高效的 “搬運工”,主要用于實現(xiàn)移動語義和完美轉(zhuǎn)發(fā) 。

移動語義是右值引用的一個重要應(yīng)用。在 C++ 中,當(dāng)我們創(chuàng)建一個對象時,會為它分配內(nèi)存等資源,在對象銷毀時,會釋放這些資源。當(dāng)我們進行對象拷貝時,會復(fù)制這些資源,這在資源比較大時會消耗很多時間和內(nèi)存。而移動語義通過右值引用,可以直接將一個對象的資源 “轉(zhuǎn)移” 給另一個對象,而不是進行拷貝,大大提高了效率。

比如我們有一個MyString類,用來管理字符串:

class MyString {
public:
    MyString(const char* str = nullptr) {
        if (str) {
            m_length = strlen(str);
            m_data = new char[m_length + 1];
            strcpy(m_data, str);
        } else {
            m_length = 0;
            m_data = new char[1];
            *m_data = '\0';
        }
    }
    MyString(const MyString& other) {
        m_length = other.m_length;
        m_data = new char[m_length + 1];
        strcpy(m_data, other.m_data);
    }
    MyString(MyString&& other) noexcept {
        m_length = other.m_length;
        m_data = other.m_data;
        other.m_length = 0;
        other.m_data = nullptr;
    }
    ~MyString() {
        delete[] m_data;
    }
private:
    char* m_data;
    size_t m_length;
};

在這個類中,我們定義了拷貝構(gòu)造函數(shù)和移動構(gòu)造函數(shù) 。拷貝構(gòu)造函數(shù)MyString(const MyString& other)會復(fù)制other對象的資源,而移動構(gòu)造函數(shù)MyString(MyString&& other) noexcept會直接將other對象的資源 “轉(zhuǎn)移” 給當(dāng)前對象,other對象的資源會被清空 。這樣,當(dāng)我們使用右值來初始化MyString對象時,就會調(diào)用移動構(gòu)造函數(shù),避免了不必要的拷貝 。例如:

MyString getString() {
    return MyString("Hello");
}
int main() {
    MyString str1 = getString(); 
    MyString str2 = std::move(str1); 
    return 0;
}

在這個例子中,getString函數(shù)返回一個右值,str1使用這個右值進行初始化,會調(diào)用移動構(gòu)造函數(shù) 。std::move函數(shù)可以將左值轉(zhuǎn)換為右值引用,str2使用std::move(str1)進行初始化,也會調(diào)用移動構(gòu)造函數(shù),將str1的資源轉(zhuǎn)移給str2 ,之后str1就處于一個有效但未定義的狀態(tài) 。

完美轉(zhuǎn)發(fā)也是右值引用的一個重要應(yīng)用。在模板編程中,有時候我們需要將參數(shù)原封不動地傳遞給另一個函數(shù),這時候就可以使用右值引用和std::forward來實現(xiàn)完美轉(zhuǎn)發(fā),保留參數(shù)的左值或右值屬性 。比如:

void process(int& value) {
    std::cout << "Processing lvalue: " << value << std::endl;
}
void process(int&& value) {
    std::cout << "Processing rvalue: " << value << std::endl;
}
template<typename T>
void forward(T&& arg) {
    process(std::forward<T>(arg));
}
int main() {
    int num = 10;
    forward(num); 
    forward(20); 
    return 0;
}

在這個例子中,forward 函數(shù)使用右值引用 T&& arg 來接收參數(shù),std::forward<T>(arg) 會根據(jù) arg 的實際類型(左值還是右值)來轉(zhuǎn)發(fā)參數(shù),這樣 process 函數(shù)就能正確地處理左值和右值 。

四、從std::move 開始:移動語義的奧秘

在了解了左值和右值之后,我們就可以正式來認(rèn)識std::move和std::forward了。首先,讓我們聚焦于std::move,看看這個神奇的工具是如何在 C++ 中施展它的魔力的。

4.1 std::move是什么?

std::move從表面上看,像是一個移動工具,但實際上,它的本質(zhì)是將一個左值轉(zhuǎn)換為右值引用 。它的基本模板函數(shù)定義如下:

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

這里的std::remove_reference<T>::type是用于移除T的引用修飾符,然后將其轉(zhuǎn)換為右值引用返回。簡單來說,std::move就是告訴編譯器,我們希望把一個對象當(dāng)作右值來處理,從而可以使用移動語義。 比如,在下面的代碼中:

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

int main() {
    std::string str = "Hello, World!";
    std::string anotherStr = std::move(str);

    std::cout << "str: " << str << std::endl;  // 此時str的內(nèi)容可能為空,因為資源已被移動
    std::cout << "anotherStr: " << anotherStr << std::endl;

    return 0;
}

str原本是一個左值,通過std::move將其轉(zhuǎn)換為右值引用后賦值給anotherStr,anotherStr會通過移動構(gòu)造函數(shù)來獲取str的資源,而不是進行傳統(tǒng)的拷貝操作,這樣可以大大提高效率,尤其是在處理大對象時。

4.2 std::move 的原理

std::move的實現(xiàn)原理其實非常巧妙,它是在編譯期完成的操作,并不會產(chǎn)生運行時的開銷 。它僅僅是進行了類型轉(zhuǎn)換,將左值引用轉(zhuǎn)換為右值引用,從而讓編譯器在合適的地方調(diào)用移動構(gòu)造函數(shù)或移動賦值運算符。

例如,當(dāng)我們有一個自定義類MyClass,并為其實現(xiàn)了移動構(gòu)造函數(shù)和移動賦值運算符:

class MyClass {
private:
    int* data;
    int size;

public:
    MyClass(int s) : size(s) {
        data = new int[s];
        for (int i = 0; i < s; ++i) {
            data[i] = i;
        }
    }

    // 拷貝構(gòu)造函數(shù)
    MyClass(const MyClass& other) : size(other.size) {
        data = new int[size];
        for (int i = 0; i < size; ++i) {
            data[i] = other.data[i];
        }
    }

    // 移動構(gòu)造函數(shù)
    MyClass(MyClass&& other) noexcept : size(other.size), data(other.data) {
        other.data = nullptr;
        other.size = 0;
    }

    // 移動賦值運算符
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            delete[] data;
            size = other.size;
            data = other.data;
            other.data = nullptr;
            other.size = 0;
        }
        return *this;
    }

    ~MyClass() {
        delete[] data;
    }
};

當(dāng)我們使用std::move來操作MyClass對象時:

MyClass obj1(10);
MyClass obj2 = std::move(obj1);

編譯器會識別出std::move(obj1)返回的是右值引用,從而調(diào)用MyClass的移動構(gòu)造函數(shù),將obj1的資源快速轉(zhuǎn)移到obj2,而不是進行深拷貝。 這就是std::move的原理,通過類型轉(zhuǎn)換,觸發(fā)移動語義,實現(xiàn)資源的高效轉(zhuǎn)移。

4.3 std::move的使用場景

std::move在實際編程中有著廣泛的應(yīng)用場景。在容器操作中,當(dāng)我們需要將一個容器的元素轉(zhuǎn)移到另一個容器時,使用std::move可以避免不必要的拷貝,提高效率。例如:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec1 = {1, 2, 3, 4, 5};
    std::vector<int> vec2;

    vec2 = std::move(vec1);

    std::cout << "vec1 size: " << vec1.size() << std::endl;  // vec1的大小可能變?yōu)?
    std::cout << "vec2 size: " << vec2.size() << std::endl;  // vec2獲得了vec1的元素

    return 0;
}

在自定義類型中,如果我們希望實現(xiàn)高效的資源管理,也可以使用std::move。比如前面提到的MyClass類,在需要轉(zhuǎn)移資源的地方使用std::move,可以確保資源的正確轉(zhuǎn)移和釋放。

4.4 std::move的注意事項

在使用std::move時,也有一些需要注意的地方。首先,std::move只是進行了類型轉(zhuǎn)換,它并不保證移動操作一定會發(fā)生 。如果目標(biāo)對象沒有實現(xiàn)移動構(gòu)造函數(shù)和移動賦值運算符,那么仍然會調(diào)用拷貝構(gòu)造函數(shù)和拷貝賦值運算符。

其次,對const對象使用std::move是沒有意義的,因為const對象不能被修改,移動語義也就無法生效 。例如:

const std::string str = "Hello";
std::string anotherStr = std::move(const_cast<std::string&>(str));  // 不建議這樣做,可能導(dǎo)致未定義行為

這類代碼不僅違背了 const 的語義規(guī)則,還可能引發(fā)未定義的行為。所以,在使用 std::move 時,必須保證對象具備可移動性,而且在完成移動操作后,原對象的狀態(tài)要處于可接受的范圍 —— 通常是處于有效但未明確指定的狀態(tài),此時不能再依賴它原有的內(nèi)容。

五、轉(zhuǎn)向std::forward:完美轉(zhuǎn)發(fā)的藝術(shù)

在了解了std::move之后,我們再來看看std::forward,它在 C++ 中同樣扮演著非常重要的角色,尤其是在模板函數(shù)的參數(shù)轉(zhuǎn)發(fā)方面,有著獨特的作用。

5.1 std::forward 是什么?

std::forward是 C++11 引入的一個模板函數(shù),它的主要作用是在模板函數(shù)中實現(xiàn)完美轉(zhuǎn)發(fā) 。所謂完美轉(zhuǎn)發(fā),就是能夠?qū)?shù)以其原始的左值或右值屬性傳遞給另一個函數(shù),而不會改變參數(shù)的類型和值類別 。簡單來說,std::forward就像是一個智能的搬運工,它會根據(jù)參數(shù)的原始屬性,原封不動地將參數(shù)傳遞給下一個函數(shù)。它的定義如下:

template<class T>
constexpr T&& forward(typename std::remove_reference<T>::type& t) noexcept {
    return static_cast<T&&>(t);
}

template<class T>
constexpr T&& forward(typename std::remove_reference<T>::type&& t) noexcept {
    static_assert(!std::is_lvalue_reference<T>::value, "bad forward call");
    return static_cast<T&&>(t);
}

從定義中可以看出,std::forward通過std::remove_reference移除參數(shù)的引用修飾符,然后根據(jù)參數(shù)是左值還是右值,使用static_cast將其轉(zhuǎn)換為正確的引用類型返回 。

5.2 std::forward 的原理

std::forward的原理涉及到模板類型推導(dǎo)和引用折疊規(guī)則 。在模板函數(shù)中,當(dāng)我們使用T&&作為參數(shù)類型時,這個參數(shù)被稱為萬能引用(也叫轉(zhuǎn)發(fā)引用) 。它既可以綁定左值,也可以綁定右值 。當(dāng)實參是左值時,T會被推導(dǎo)為左值引用類型,此時T&&會根據(jù)引用折疊規(guī)則折疊為左值引用;當(dāng)實參是右值時,T會被推導(dǎo)為非引用類型,T&&保持為右值引用 。

例如:

template<typename T>
void func(T&& param) {
    anotherFunc(std::forward<T>(param));
}

當(dāng)調(diào)用func(a)(a是左值)時,T被推導(dǎo)為int&,std::forward<T>(param)返回的是左值引用;當(dāng)調(diào)用func(10)(10是右值)時,T被推導(dǎo)為int,std::forward<T>(param)返回的是右值引用 。這樣,anotherFunc函數(shù)就能夠接收到與原始參數(shù)相同類型和值類別的參數(shù),實現(xiàn)了完美轉(zhuǎn)發(fā) 。

5.3 std::forward的使用場景

std::forward在實際編程中有著廣泛的應(yīng)用場景。在實現(xiàn)通用的函數(shù)模板時,我們經(jīng)常需要將參數(shù)轉(zhuǎn)發(fā)給其他函數(shù),這時候std::forward就派上用場了 。比如,在實現(xiàn)一個簡單的工廠函數(shù)時:

#include <iostream>
#include <memory>

class Product {
public:
    Product() {
        std::cout << "Product constructor" << std::endl;
    }
};

template<typename... Args>
std::unique_ptr<Product> createProduct(Args&&... args) {
    return std::make_unique<Product>(std::forward<Args>(args)...);
}

int main() {
    auto product = createProduct();

    return 0;
}

在這個例子中,createProduct函數(shù)使用std::forward將參數(shù)完美轉(zhuǎn)發(fā)給std::make_unique<Product>,這樣無論調(diào)用createProduct時傳入的是左值還是右值,都能正確地構(gòu)造Product對象 。

5.4 std::forward 的注意事項

在使用std::forward時,有一點需要特別注意,那就是必須顯式指定模板參數(shù)類型 。如果不指定模板參數(shù)類型,編譯器無法推導(dǎo)出正確的值類別,可能會導(dǎo)致轉(zhuǎn)發(fā)失敗 。例如:

template<typename T>
void func(T&& param) {
    // 錯誤,沒有指定模板參數(shù)類型
    anotherFunc(std::forward(param));
}

正確的做法是:

template<typename T>
void func(T&& param) {
    anotherFunc(std::forward<T>(param));
}

只有顯式指定了模板參數(shù)類型T,std::forward才能根據(jù)T的類型正確地轉(zhuǎn)發(fā)參數(shù) 。

六、對比 std::move 和 std::forward

在深入理解了 std::move 與 std::forward 各自的特性后,我們來對這兩者做一次全面對比,這樣能更清晰地把握這兩個工具在 C++ 編程中的具體應(yīng)用。

(1)功能對比:std::move的功能是將一個左值無條件地轉(zhuǎn)換為右值引用,其目的是為了啟用移動語義,允許資源從一個對象高效地轉(zhuǎn)移到另一個對象 。而std::forward的功能則是在模板函數(shù)中,根據(jù)模板參數(shù)的推導(dǎo)結(jié)果,有條件地將參數(shù)轉(zhuǎn)換為右值引用,從而實現(xiàn)完美轉(zhuǎn)發(fā),確保參數(shù)在傳遞過程中保持其原始的值類別(左值或右值) 。簡單來說,std::move是一種強制轉(zhuǎn)換,而std::forward是一種智能的、有條件的轉(zhuǎn)換 。

(2)返回類型對比:std::move始終返回右值引用類型T&&,無論傳入的參數(shù)是左值還是右值 。而std::forward的返回類型則依賴于模板參數(shù)T的推導(dǎo)結(jié)果,如果T被推導(dǎo)為左值引用類型,那么std::forward<T>返回左值引用;如果T被推導(dǎo)為非引用類型或右值引用類型,那么std::forward<T>返回右值引用 。這使得std::forward能夠根據(jù)參數(shù)的原始類型,準(zhǔn)確地返回相應(yīng)的引用類型 。

(3)使用場景對比:std::move主要用于移動語義相關(guān)的場景,比如在實現(xiàn)移動構(gòu)造函數(shù)和移動賦值運算符時,以及在需要將一個對象的資源轉(zhuǎn)移給另一個對象時 。而std::forward則主要用于模板函數(shù)中需要完美轉(zhuǎn)發(fā)參數(shù)的場景,確保參數(shù)能夠以其原始的左值或右值屬性傳遞給其他函數(shù) 。例如,在實現(xiàn)通用的函數(shù)模板、工廠函數(shù)、函數(shù)包裝器等場景中,std::forward發(fā)揮著重要的作用 。

簡單來說:

  • std::move:無條件轉(zhuǎn)換為右值引用。它的核心是“移動語義”。
  • std::forward:有條件地(完美)轉(zhuǎn)發(fā)參數(shù)。它的核心是“保持原始值類別”。

圖片圖片

6.1 std::move

它的功能很簡單:把一個左值(即有名稱且可獲取地址的對象)標(biāo)記為右值(即臨時的、即將被銷毀的值)。這一操作能讓編譯器選擇使用移動構(gòu)造函數(shù)或移動賦值運算符,而非拷貝構(gòu)造函數(shù),進而實現(xiàn)資源的高效轉(zhuǎn)移。

template <typename T>
typename std::remove_reference<T>::type&& move(T&& t) noexcept {
    // 無論 T 是左值引用類型還是非引用類型,
    // remove_reference<T>::type 都會得到其根本的類型 U。
    // 最后強制轉(zhuǎn)換為 U&& 并返回。
    return static_cast<typename std::remove_reference<T>::type&&>(t);
}

它通過 static_cast 強行將任何類型 T 的表達式轉(zhuǎn)換為其對應(yīng)的右值引用類型。

std::string str1 = "Hello";
std::string str2 = std::move(str1); // 調(diào)用 string 的移動構(gòu)造函數(shù)

// move() 之后,str1 的狀態(tài)是有效的但未指定的(valid but unspecified)。
// 通常 str1 變?yōu)榭兆址?,但你不能依賴這一點,只能對它進行銷毀或重新賦值。

6.2 std::forward

它一般與 “通用引用”(函數(shù)模板參數(shù)中形如 T&& 的形式)搭配使用。通用引用有個特殊性質(zhì):能夠依據(jù)實參的值類別來進行推導(dǎo)。

  • 如果傳遞來的是一個左值,T 被推導(dǎo)為 U&,根據(jù)引用折疊規(guī)則,T&& => U&。
  • 如果傳遞來的是一個右值,T被推導(dǎo)為 U或 U&&, T&& => U&&.

std::forward<T> 的任務(wù)就是:如果參數(shù) originally was an lvalue(即 T 被推導(dǎo)為左值引用類型),它就返回一個左值引用;如果參數(shù) originally was an rvalue(即 T 被推導(dǎo)為非引用類型),它就返回一個右值引用。這樣就完美地保持了參數(shù)原始的值類別。

// 重載版本1:當(dāng) T 不是左值引用類型時(即原始參數(shù)是右值時)
template <class T>
T&& forward(typename std::remove_reference<T>::type&& t) noexcept {
    return static_cast<T&&>(t);
}

//重載版本2:當(dāng) T是左值時
  template <class T>
  constexpr T && forward( remove_reference_t< T > & t ) noexcept{
      return static_cast< T && >( t );
  }

它利用模板特化和引用折疊規(guī)則來實現(xiàn)有條件轉(zhuǎn)換。

比如:
   void bar( widget && w , widget & w1 );//接收一個右值和一左值的bar函數(shù)

   template< typename T1, typename T2 >
   void foo( T1 && a, T2 && b ){//a和b都是通用引用
       bar( std::forward< T1 >( a ), std::forward< T2 >( b ) );
   }

   widget w;
   foo( widget(), w ); 
   /* 
     對于第一個實參widget(): 
        它是純右值->T1被推導(dǎo)為widget -> forward版本1被實例化: widget&& forward(widget&& t)
        static_cast<widget&&>(t) ->返回widget&&

     對于第二個實參w:
        它是左值->T2被推導(dǎo)為widget& -> forward版本2被實例化: widget& forward(widget& t)
        static_cast<widget& &&>(t) ->應(yīng)用折疊規(guī)則: static_cast<widget&>(t) ->返回widget&
    */

示例 (Perfect Forwarding): 沒有 std::forward,會導(dǎo)致值類別信息丟失。

#include <iostream>
#include <utility>

void process(int& i) {
	std::cout << "處理左值: " << i << std::endl;
}
void process(int&& i) {
	std::cout << "處理右値: " << i << std::endl;
}

// 【糟糕的轉(zhuǎn)發(fā)】:丟失了値類別信息
template <typename T>
void bad_forwarder(T t) { // By-value接收,會創(chuàng)建副本,原値類別信息完全丟失
	process(t); // t始終是一個左値,因此總是調(diào)用 process(int&)
}

// 【完美的轉(zhuǎn)發(fā)】
template <typename T>
void good_forwarder(T&& t) { // Universal reference接收,保留値類別信息
	process(std::forward<T>(t)); // 使用 forward保持t的原始値類別
}

int main() {
	int a = 10;

	bad_forwarder(a); //輸出:“處理左値:10”
	bad_forwarder(20); //輸出:“處理左値:20” 錯誤!我們希望它調(diào)用右値版本

	good_forwarder(a); //輸出:“處理左値:10” 
	good_forwarder(20); //輸出:“處理右值:20” 正確!
}

七、高配面試題講解

題目1:什么是左值(lvalue)和右值(rvalue)?

  • 左值:指有標(biāo)識符、可被取地址的表達式,代表一個持久存在的對象,可出現(xiàn)在賦值運算符左側(cè)。例如:變量名(int a = 5;中的a)、數(shù)組元素(arr[0])、返回左值引用的函數(shù)調(diào)用。
  • 右值:指無標(biāo)識符、不可被取地址的臨時值,通常是字面量或表達式的臨時結(jié)果,只能出現(xiàn)在賦值運算符右側(cè)。例如:字面量(5)、表達式結(jié)果(a + b)、臨時對象(std::string("hello"))。

題目2:左值引用(T&)和右值引用(T&&)的區(qū)別是什么?

  • 左值引用(T&):只能綁定左值,用于延長左值的生命周期(如函數(shù)返回左值引用)。
  • 右值引用(T&&):只能綁定右值(臨時對象或被std::move轉(zhuǎn)換的左值),用于實現(xiàn)移動語義,避免不必要的拷貝。

題目3:std::move的作用是什么?它會移動對象嗎?

  • std::move的作用是將左值強制轉(zhuǎn)換為右值引用,標(biāo)記對象可被移動,而非實際 “移動” 數(shù)據(jù)。
  • 它本身不執(zhí)行移動操作,僅允許編譯器選擇移動構(gòu)造函數(shù) / 賦值運算符,真正的資源轉(zhuǎn)移由移動語義實現(xiàn)。

題目4:為什么右值引用可以延長臨時對象的生命周期?

C++ 標(biāo)準(zhǔn)規(guī)定:當(dāng)右值引用綁定到一個臨時對象時,該臨時對象的生命周期會被延長至與右值引用相同,避免臨時對象過早銷毀。例如:

const std::string& ref1 = std::string("temp"); // 左值引用延長生命周期(C++98起)
std::string&& ref2 = std::string("temp");      // 右值引用同樣延長生命周期

題目5:什么是 “通用引用”(Universal Reference)?它與右值引用有何區(qū)別?

  • 通用引用:形如T&&的引用,僅在模板參數(shù)推導(dǎo)或auto推導(dǎo)場景下存在(如template <typename T> void f(T&& x)),可根據(jù)實參類型(左值 / 右值)自動推導(dǎo)為左值引用或右值引用。
  • 區(qū)別:右值引用是確定的類型(T&&),只能綁定右值;通用引用是 “可推導(dǎo)的引用”,可綁定左值或右值。

題目6:std::forward的作用是什么?何時使用?

std::forward用于 “完美轉(zhuǎn)發(fā)”,在模板函數(shù)中保持實參的原始值類別(左值 / 右值),避免因傳遞過程中值類別被改變而導(dǎo)致的錯誤(如意外觸發(fā)拷貝而非移動)。通常與通用引用配合使用,例如:

template <typename T>
void wrapper(T&& x) {
    func(std::forward<T>(x)); // 保持x的原始值類別
}

題目7:以下代碼中x是左值還是右值?func(x)調(diào)用的是哪個重載?

void func(int&) { cout << "左值引用"; }
void func(int&&) { cout << "右值引用"; }

int main() {
    int&& x = 5;
    func(x); 
}
  • x是左值。盡管x的類型是右值引用,但它有名稱、可被取地址(&x合法),符合左值特征。
  • 調(diào)用func(int&),輸出 “左值引用”。

題目8:什么是 “值類別塌陷”(Reference Collapsing)?它與通用引用有何關(guān)系?

  • 值類別塌陷是模板推導(dǎo)中引用類型的轉(zhuǎn)換規(guī)則:T& &→T&,T& &&→T&,T&& &→T&,T&& &&→T&&。
  • 通用引用(T&&)依賴此規(guī)則實現(xiàn):當(dāng)實參是左值(T&),推導(dǎo)后T&&塌陷為T&;當(dāng)實參是右值(T),推導(dǎo)后保持T&&,從而實現(xiàn) “左值綁定左值引用,右值綁定右值引用”。

題目9:完美轉(zhuǎn)發(fā)(Perfect Forwarding)的目的是什么?如何實現(xiàn)?

  • 目的:在模板函數(shù)中傳遞參數(shù)時,保持實參原始的左值 / 右值屬性,避免不必要的拷貝或移動。
  • 實現(xiàn):結(jié)合通用引用(T&&)和std::forward,例如:
template <typename T>
void wrap(T&& arg) {
    target(std::forward<T>(arg)); // 保持arg的原始值類別
}

題目10:左值能否被移動?如何實現(xiàn)?

能。左值本身不能直接綁定到右值引用,但可通過std::move將其轉(zhuǎn)換為右值引用,從而觸發(fā)移動操作。例如:

std::vector<int> a = {1,2,3};
std::vector<int> b = std::move(a); // a是左值,經(jīng)std::move轉(zhuǎn)換后被移動

題目11:移動語義與拷貝語義的核心區(qū)別是什么?何時該使用移動語義?

  • 核心區(qū)別:拷貝語義復(fù)制資源(如深拷貝動態(tài)內(nèi)存),移動語義轉(zhuǎn)移資源所有權(quán)(如直接接管指針,原對象資源被置空),效率更高。
  • 使用場景:當(dāng)對象是臨時的(右值)或明確不再使用(通過std::move標(biāo)記的左值)時,使用移動語義可避免冗余拷貝(如容器擴容、函數(shù)返回大對象)。

這些問題覆蓋了左值 / 右值的基礎(chǔ)概念、引用規(guī)則及現(xiàn)代 C++ 特性的應(yīng)用,是面試中考察內(nèi)存管理與性能優(yōu)化能力的常見考點。

責(zé)任編輯:武曉燕 來源: 深度Linux
相關(guān)推薦

2025-06-06 07:35:06

C++表達式右值

2023-10-04 00:38:30

C++原子

2024-01-24 12:30:18

C++開發(fā)

2024-02-26 00:05:00

C++開發(fā)

2025-06-26 04:10:00

2023-11-21 16:13:38

C++代碼

2023-12-13 10:08:59

C++原子代碼

2023-11-24 16:13:05

C++編程

2025-04-07 01:11:00

右值C++泛型

2024-12-24 07:20:00

C++std::anyC++17

2022-09-19 08:12:47

編譯器程序函數(shù)

2024-10-14 15:04:15

C++std::any存儲

2021-07-16 07:21:45

C++可調(diào)用對象std::functi

2025-03-10 08:30:00

2021-08-30 07:59:56

C++溢出函數(shù)

2023-10-25 13:27:20

C++字符串

2009-08-13 17:30:30

C#構(gòu)造函數(shù)

2021-07-30 06:22:37

C++字符型字符串

2024-01-26 11:08:57

C++函數(shù)返回不同類型

2011-07-14 10:12:50

C++
點贊
收藏

51CTO技術(shù)棧公眾號

日韩一级特黄毛片| 91精品免费看| 日本一区二区三区网站| 成人mm视频在线观看| 成人欧美一区二区三区1314| 成人免费在线看片| 久久久成人免费视频| 久久免费精品视频在这里| 日韩欧美自拍偷拍| 国产成人黄色片| 国产成人l区| 99精品视频免费在线观看| 国产精品视频永久免费播放| 青娱乐国产在线| 精品国内自产拍在线观看视频| 制服丝袜亚洲精品中文字幕| 高清在线观看免费| 91最新在线视频| 国产日韩综合av| 国产美女精品久久久| 夜夜嗨aⅴ一区二区三区| 激情综合自拍| 久久久国产精品视频| mm131美女视频| 爱高潮www亚洲精品| 欧美精品xxxxbbbb| 日韩免费高清在线| 免费h在线看| 一区二区三区毛片| 一道精品一区二区三区 | 91精品国产综合久久福利软件 | 日本二区三区视频| 要久久电视剧全集免费| 欧美变态口味重另类| 欧美性受xxxxxx黑人xyx性爽| 亚洲美女炮图| 五月婷婷综合在线| 91免费国产精品| 视频一区二区免费| 成人免费福利片| 99久久国产免费免费| 国产精品欧美亚洲| 美女视频黄频大全不卡视频在线播放| 欧美在线视频观看| 国产精品久久久免费视频| 在线观看视频免费一区二区三区 | heyzo一区| 一区二区三区中文字幕在线观看| 在线视频不卡一区二区三区| yw193.com尤物在线| 国产欧美日韩另类视频免费观看| 欧美精品成人一区二区在线观看| 在线观看xxx| 成人av在线资源| 国产亚洲二区| 五月婷在线视频| 99精品国产99久久久久久白柏| www.久久久| 欧美在线 | 亚洲| 成人h动漫精品一区二| 狠狠色综合色区| 天天干,夜夜操| 2024国产精品| 日本一区视频在线观看| 成人在线免费看| 亚洲欧洲日韩女同| 性生活免费观看视频| 影院在线观看全集免费观看| 夜夜揉揉日日人人青青一国产精品| 国产日产欧美一区二区| 男人天堂亚洲天堂| 欧美日韩国产一区在线| 日韩中文字幕二区| 日本免费一区二区三区等视频| 欧美日本国产视频| 久久无码人妻一区二区三区| av在线亚洲色图| 日韩av在线直播| 久久婷婷五月综合| 91精品国产视频| 国内精品久久影院| 亚洲欧美一区二区三区在线观看| 日av在线不卡| 亚洲a成v人在线观看| 免费国产羞羞网站视频| www日韩大片| 手机在线视频你懂的| 福利在线导航136| 在线影视一区二区三区| 伊人成人免费视频| 中文有码一区| 欧美大奶子在线| 成人免费毛片男人用品| 激情综合亚洲精品| 精品国产乱码久久久久久郑州公司 | 视频精品一区二区| 亚洲一区二区三区sesese| 婷婷色在线观看| 亚洲欧洲日韩女同| 成人毛片视频网站| 99综合久久| 国产丝袜一区视频在线观看 | 欧美日韩亚洲视频一区| 婷婷激情综合五月天| 精品日产乱码久久久久久仙踪林| 伊人久久大香线蕉av一区二区| 久久午夜鲁丝片午夜精品| 日韩和欧美一区二区| 99国产视频| 日本高清视频在线播放| 精品久久久久久久久久久| 国产性生活一级片| 精品欧美久久| 亚洲91av视频| 99精品视频免费看| 国产欧美日韩不卡免费| 妞干网在线观看视频| 四虎在线精品| 亚洲码在线观看| 久久久久亚洲av成人片| 久久99热这里只有精品| 欧美深深色噜噜狠狠yyy| 黄色羞羞视频在线观看| 91麻豆精品国产91久久久 | 国产九九热视频| 妖精一区二区三区精品视频| 色综合久久精品亚洲国产| 国产又粗又猛又色又| 久久午夜羞羞影院免费观看| 成品人视频ww入口| 亚洲综合影院| 久久国产精品电影| 国产一区二区自拍视频| 国产日韩欧美亚洲| 日韩在线第三页| 色综合综合网| 日韩av电影在线播放| 香港一级纯黄大片| 亚洲成人精品影院| 伊人久久一区二区三区| 国产专区一区| 国产精品久久久久久久久婷婷 | 日本a口亚洲| 国产精品福利观看| 激情在线视频| 在线观看成人小视频| 欧洲美一区二区三区亚洲| 亚洲一区网站| 欧美激情第六页| 欧美亚洲韩国| 中文字幕一区二区三区电影| 国产精品成人无码| 国产精品久线在线观看| 777一区二区| 一区二区三区在线电影| 亚洲最大av在线| 欧美xxxx少妇| 亚洲精品国产精品国自产观看浪潮| 国产午夜福利一区二区| 99久久精品久久久久久清纯| 夫妻免费无码v看片| 久久不卡国产精品一区二区 | 欧美视频二区欧美影视| 九九久久久久99精品| 蜜臀av免费在线观看| 精品久久久久久久久久久久久| www.自拍偷拍| 蜜桃一区二区三区在线观看| 在线免费观看成人| 国产成人一二| 日本中文字幕成人| 欧美激情免费| 亚洲国产精品系列| 国产亚洲欧美在线精品| 中文字幕在线观看一区二区| 日本女人黄色片| 日韩一级免费| 亚洲乱码国产乱码精品天美传媒| 国产精品免费精品自在线观看| 欧美国产日韩在线| 你懂的视频在线| 欧美高清视频不卡网| 国产精品成人av久久| 国产欧美日韩视频在线观看| 无套白嫩进入乌克兰美女| 99香蕉国产精品偷在线观看| 日韩亚洲一区在线播放| 色悠久久久久综合先锋影音下载| 51精品在线观看| 黄色网在线看| 亚洲免费小视频| av中文字幕免费在线观看| 精品久久久香蕉免费精品视频| 国产传媒在线看| 高清免费成人av| 欧美日韩亚洲自拍| 伊人久久婷婷| 一区二区欧美日韩| 伊人久久大香线蕉av不卡| 18成人在线| 中文字幕av一区二区三区佐山爱| 成年人精品视频| 国产福利小视频在线| 欧美精品一区二区三区一线天视频 | yellow视频在线观看一区二区| 成人性教育av免费网址| 久久99热精品| 伊人免费在线| 亚洲欧美国产精品久久久久久久| a网站在线观看| 欧美无乱码久久久免费午夜一区| 精品无码av在线| 国产精品久久久久aaaa樱花 | 精品成av人一区二区三区| av资源站久久亚洲| 黄色成人小视频| 欧美做受高潮1| 国产色婷婷在线| 久久精品国产亚洲一区二区| 日韩二区三区| 日韩av在线不卡| 国产999久久久| 欧美剧在线免费观看网站| 中文字幕黄色片| 欧美色道久久88综合亚洲精品| 亚洲成人生活片| 亚洲婷婷综合久久一本伊一区| 级毛片内射视频| 91免费观看视频在线| 欧美xxxxx精品| 国产aⅴ精品一区二区三区色成熟| 91制片厂毛片| 天堂一区二区在线| 免费在线观看毛片网站| 99在线|亚洲一区二区| 久久成人福利视频| 欧美人成网站| 免费人成自慰网站| 亚洲性图久久| 丝袜人妻一区二区三区| 好吊视频一区二区三区四区| 国产a级黄色大片| 欧美日韩免费| www.在线观看av| 国产精品v亚洲精品v日韩精品| 亚洲黄色网址在线观看| 亚洲经典一区| 国产91视频一区| 亚洲国产免费看| 成年人午夜视频在线观看| 国产精品尤物| 无码少妇一区二区三区芒果| 日韩激情av在线| 中文字幕第88页| 韩国一区二区视频| 黄页网站在线看| 国产91露脸合集magnet| 国产精品扒开腿做爽爽爽a片唱戏 亚洲av成人精品一区二区三区 | 91精品国产三级| 国产一级精品在线| 在线观看欧美一区二区| 北岛玲一区二区三区四区| 亚洲一区二区在线免费| 久久综合九色综合97_久久久| 在线观看日韩精品视频| 日本一区二区三区视频视频| 日本免费网站视频| 一片黄亚洲嫩模| 天天操天天摸天天干| 欧美伊人久久久久久午夜久久久久| 国产精品无码粉嫩小泬| 91精品国产综合久久婷婷香蕉| 午夜精品久久久久久久96蜜桃| 亚洲国产私拍精品国模在线观看| 免费在线国产| 久久精品国产v日韩v亚洲 | 国产精品成人观看视频国产奇米| 激情中国色综合| 99中文字幕| 五月天亚洲色图| 一区二区三区|亚洲午夜| 亚洲午夜电影| 爱情岛论坛亚洲首页入口章节| 国产乱子轮精品视频| 毛茸茸多毛bbb毛多视频| 国产精品国产馆在线真实露脸| 国产真实夫妇交换视频 | a级片在线播放| 亚洲欧美成人精品| 中文字幕有码在线视频| 538国产精品一区二区免费视频| 国产精品伦一区二区| 国产福利一区二区三区在线观看| 国精一区二区| 17c丨国产丨精品视频| 日本欧美在线观看| www.四虎精品| 国产精品三级久久久久三级| 国产精品第二十页| 67194成人在线观看| 日本中文字幕电影在线观看| 久久精品国产久精国产思思| 中文字幕资源网在线观看免费| 91精品国产综合久久香蕉| 亚洲8888| 国产免费黄色一级片| 久久99精品国产.久久久久久| 国产福利在线观看视频| 亚洲精品老司机| 亚洲视屏在线观看| 日韩精品视频在线免费观看| 日本欧美电影在线观看| 国产一区二区丝袜| 欧美**vk| 国产精品无码一区二区在线| 国产成人精品1024| 午夜国产福利视频| 色噜噜狠狠色综合欧洲selulu| 亚洲成a人片在线| 久久久av网站| 九九热这里有精品| 色视频一区二区三区| 亚洲一区二区网站| 欧美xxxxx少妇| 一区二区免费视频| 国产麻豆精品一区| www.久久色.com| 91精品国产66| 日韩电影在线播放| 久久一区二区三区超碰国产精品| 黄色激情在线观看| 一二三区精品视频| 亚洲国产综合网| 久久成人精品电影| 精品国产鲁一鲁****| 欧美 另类 交| 精品一区二区免费在线观看| 男人天堂资源网| 欧美日韩综合不卡| 啊v视频在线| 91精品国产综合久久香蕉的用户体验 | 日本午夜精品一区二区| 美女久久一区| 精品人妻无码一区二区三区 | 国产主播性色av福利精品一区| 色一情一乱一乱一区91| 国产成人在线看| 国产在线视频第一页| 亚洲国内高清视频| 蜜桃视频在线观看播放| 欧美日韩精品免费看| 久久美女性网| 波多野结衣家庭教师在线观看| 欧美三级日韩三级| 国产精品va在线观看视色| 亚洲伊人第一页| 亚洲国产专区校园欧美| 亚洲一区二区三区四区五区六区| 色综合久久久久综合99| 番号集在线观看| 成人免费在线视频网站| 欧美三级免费| 我和岳m愉情xxxⅹ视频| 在线观看91精品国产入口| 日本视频在线播放| 粉嫩av四季av绯色av第一区| 亚洲人成免费| 男女做爰猛烈刺激| 欧美挠脚心视频网站| 性欧美1819sex性高清大胸| 国产精品嫩草在线观看| 久久精品天堂| 北条麻妃在线观看视频| 日韩欧美精品在线| 超碰aⅴ人人做人人爽欧美| 午夜精品一区二区三区在线观看 | 天天躁夜夜躁狠狠是什么心态| 欧美日韩一区二区在线观看视频| 国产原创精品视频| 精品国产免费人成电影在线观...| 蜜乳av另类精品一区二区| 麻豆视频免费在线播放| 日韩欧美在线123| 欧美1级2级| 久久久99精品视频| 久久综合久久久久88| 国产又粗又猛又黄又爽无遮挡| 国内自拍欧美激情| 日韩国产专区| 日韩精品视频一区二区| 欧美日韩一本到| 国产白浆在线免费观看| 亚洲国产欧美日韩| 波多野结衣中文字幕一区| 亚洲天堂视频网| 97国产精品视频| 久久久久久免费视频| 亚洲第一页av| 日韩欧美中文字幕制服| 成人亚洲网站|