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

C++11初探:C++發(fā)展史上的里程碑

 更新時間:2026年07月29日 09:51:19   作者:jinyishu_  
本文將從C++11的歷史背景講起,一路深入到統(tǒng)一初始化、右值引用、移動語義、萬能引用、完美轉(zhuǎn)發(fā) 這些核心機制,幫助你奠定理解C++11的基礎,感興趣的朋友跟隨小編一起看看吧

一、C++11:十年磨一劍

如果你問一個C++程序員哪個版本的C++最重要,他大概率會脫口而出:C++11

這不是沒有道理的。在C++11之前,C++98/03標準已經(jīng)用了整整十幾年。這期間,其他編程語言如同雨后春筍般涌現(xiàn):Java憑借JVM的跨平臺和自動垃圾回收攻城略地,Python靠著簡潔的語法和無數(shù)的第三方庫異軍突起,C#在Windows生態(tài)里如魚得水。而C++呢?還是那套"老古董"標準,程序員們一邊用著"落后的生產(chǎn)力",一邊眼巴巴望著Boost庫里的好東西進不了標準。

終于,在2011年,C++11標準正式發(fā)布。這個版本的更新量之大,被人戲稱為"像是一門新語言"。C++之父Bjarne Stroustrup甚至說:C++11 feels like a new language.

這次更新涵蓋了核心語言、標準庫兩大層面的海量特性。本文將從 C++11的歷史背景 講起,一路深入到 統(tǒng)一初始化、右值引用、移動語義、萬能引用、完美轉(zhuǎn)發(fā) 這些核心機制,幫助你奠定理解C++11的基礎。

二、統(tǒng)一的列表初始化

2.1 C++98的初始化之痛

C++98里,初始化語法簡直可以用"五花八門"來形容:

// C++98支持的初始化方式
int a1[] = { 1, 2, 3, 4, 5 };   // 數(shù)組可以用{}
int a2[5] = { 0 };               // 部分初始化
Point p = { 1, 2 };              // 聚合體(struct)可以用{}
int x = 2;                       // 基本類型用=
// 但是STL容器呢?
vector<int> v;                   // 只能先構造空容器
v.push_back(1);                  // 再一個個插入
v.push_back(2);
v.push_back(3);
// 沒法像數(shù)組一樣 vector<int> v = {1, 2, 3};  ??

你能發(fā)現(xiàn)什么問題?數(shù)組能用 {},結(jié)構體能用 {},偏偏 vector 不能用 {}! 這種不一致性讓無數(shù)新手抓狂,也讓老手在寫代碼時心里總繃著一根弦。

2.2 C++11:一切皆可{}

C++11引入了 std::initializer_list,徹底統(tǒng)一了初始化語法:

#include <vector>
#include <map>
#include <string>
#include <iostream>
using namespace std;
struct Point {
    int _x;
    int _y;
};
class Date {
public:
    Date(int year = 1, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {
        cout << "Date(int year, int month, int day)" << endl;
    }
    Date(const Date& d)
        : _year(d._year), _month(d._month), _day(d._day) {
        cout << "Date(const Date& d)" << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main() {
    // ===== 內(nèi)置類型也支持{} =====
    int x1 = { 2 };   // C++11新支持
    int x2 = 2;       // 傳統(tǒng)方式
    // ===== 自定義類型支持{} =====
    // {2025, 1, 1}構造一個Date臨時對象,然后拷貝給d1
    // 但編譯器通常會優(yōu)化成直接構造d1(拷貝省略)
    Date d1 = { 2025, 1, 1 };
    Date d20(2025, 1, 1);     // 等價寫法
    // 引用綁定到{}創(chuàng)造的臨時對象上
    const Date& d2 = { 2024, 7, 25 };
    // 隱式類型轉(zhuǎn)換也支持{}
    Date d3 = { 2025 };  // C++11 style
    Date d4 = 2025;      // C++98 style(單參數(shù)隱式轉(zhuǎn)換)
    // ===== 省略=號也OK =====
    Point p1{ 1, 2 };
    int x3{ 2 };
    Date d6{ 2024, 7, 25 };
    const Date& d7{ 2024, 7, 25 };
    // ===== STL容器的好日子來了 =====
    vector<int> v1{ 1, 2, 3, 4 };
    vector<int> v2{ 10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
    const vector<int>& v4{ 10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
    // 函數(shù)傳參時用{}簡直不要太爽
    vector<Date> v;
    v.push_back(d1);                    // 傳已有對象
    v.push_back(Date(2025, 1, 1));      // 傳匿名對象
    v.push_back({ 2025, 1, 1 });        // {}語法——最短最直觀!
    map<string, string> dict;
    dict.insert({ "xxx", "yyyy" });     // 鍵值對一氣呵成
    // 甚至可以直接用{}初始化整個map
    map<string, string> dict2 = {
        { "xxx",  "yyyy" },
        { "sort", "zzzz" }
    };
    return 0;
}

2.3initializer_list究竟是何方神圣?

你以為 {1, 2, 3, 4} 是啥?它本質(zhì)上就是一個 std::initializer_list<int> 對象。

initializer_list<int> il = { 10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
cout << typeid(il).name() << endl;  // 看看它的真名

initializer_list 的背后,編譯器會幫你在棧上創(chuàng)建一個底層常量數(shù)組,然后 initializer_list 對象只是保存了指向這個數(shù)組的起始指針和長度。所以它非常輕量,拷貝一個 initializer_list 也只是拷貝指針和長度,不會拷貝實際數(shù)據(jù)。

為了讓自定義容器也支持 {} 初始化,你只需要添加一個接受 initializer_list 的構造函數(shù):

template<class T>
class list {
public:
    list(initializer_list<T> il) {
        empty_init();
        for (auto& e : il) {
            push_back(e);
        }
    }
    // ...
};
// 現(xiàn)在就可以這樣用了!
list<int> lt0({ 1, 2, 3, 4, 5, 6 });  // 直接構造
list<int> lt1 = { 1, 2, 3, 4, 5, 6 };  // 隱式類型轉(zhuǎn)換

小結(jié):初始化列表統(tǒng)一了C++的初始化語法,解決了"數(shù)組能用{}容器不能用"的歷史遺留問題。它讓代碼更簡潔、更一致,是C++11最接地氣的改進之一。

三、左值與右值

在深入右值引用之前,我們必須先搞清楚一個根本問題:什么算是左值,什么算是右值?

請注意:賦值號左邊的不一定是左值,賦值號右邊的也不一定是右值。

最靠譜的判斷標準其實很簡單:

能不能用 & 取地址?能取地址的就是左值,不能的就是右值。

我們來驗證一下:

int main() {
    int* p = new int(0);
    int b = 1;
    const int c = b;
    *p = 10;
    string s("111111");
    s[0] = 'x';
    // 下面這些都能取地址——它們都是左值
    cout << &b    << endl;   // b是左值 ?
    cout << &c    << endl;   // const變量也是左值 ?
    cout << &p    << endl;   // 指針變量是左值 ?
    cout << &(*p) << endl;   // *p解引用后是左值 ?
    cout << &s    << endl;   // string對象是左值 ?
    cout << (void*)&s[0] << endl;  // 字符引用是左值 ?
}

再看看哪些不能取地址——它們就是右值:

int main() {
    double x = 1.1, y = 2.2;
    // 下面這些都不能取地址——它們都是右值
    10;              // 字面常量是右值
    x + y;           // 表達式結(jié)果是右值
    fmin(x, y);      // 函數(shù)返回值是右值
    string("11111"); // 匿名對象是右值
    // 下面這些代碼全部編譯報錯
    // cout << &10 << endl;              // error!
    // cout << &(x + y) << endl;         // error!
    // cout << &(fmin(x, y)) << endl;    // error!
    // cout << &string("11111") << endl; // error!
}

左值 = 有明確內(nèi)存地址、生命周期持續(xù)存在的對象。變量、解引用指針、數(shù)組元素等都屬于左值。

右值 = 臨時性的、沒有穩(wěn)定地址的表達式結(jié)果。字面常量、表達式臨時結(jié)果、匿名對象等都屬于右值。

四、左值引用與右值引用

4.1 左值引用T&

左值引用大家都很熟悉了,它可以綁定到左值上:

int main() {
    int* p = new int(0);
    int b = 1;
    string s("111111");
    // 左值引用綁定到左值——正常工作
    int&    r1 = b;     // ?
    int*&   r2 = p;     // ?
    int&    r3 = *p;    // ?
    string& r4 = s;     // ?
    char&   r5 = s[0];  // ?
}

普通左值引用不能直接綁定到右值:

int& r = 10;  // 編譯錯誤!

const左值引用可以綁定到右值,這是C++98就有的規(guī)則:

const int&    rx1 = 10;              // ? const左值引用能綁定右值
const double& rx2 = 1.1 + 2.2;       // ?
const double& rx3 = fmin(1.1, 2.2);  // ?
const string& rx4 = string("hello"); // ?

這個特性非常重要——它使得我們可以用const引用來接收臨時對象,避免了不必要的拷貝,同時也延長了臨時對象的生命周期。

4.2 右值引用T&&

C++11引入了右值引用,它可以直接綁定到右值:

int main() {
    double x = 1.1, y = 2.2;
    int&&    rr1 = 10;              // ? 綁定到字面常量
    double&& rr2 = x + y;           // ? 綁定到表達式結(jié)果
    double&& rr3 = fmin(x, y);      // ? 綁定到函數(shù)返回值
    string&& rr4 = string("11111"); // ? 綁定到匿名對象
}

那右值引用能不能綁定到左值呢?直接綁不行,但用 std::move() 就可以綁定了。

int main() {
    int b = 1;
    int* p = new int(0);
    string s("hello");
    // 右值引用直接綁左值 → 編譯錯誤
    // int&& rr = b;  // error!
    // 用move()把左值轉(zhuǎn)成右值(xvalue),就能綁了
    int&&    rrx1 = move(b);       // ?
    int*&&   rrx2 = move(p);       // ?
    int&&    rrx3 = move(*p);      // ?
    string&& rrx4 = move(s);       // ?
    string&& rrx5 = (string&&)s;   // 等價于move——強制類型轉(zhuǎn)換
}

4.3 一個極易忽略的細節(jié)

右值引用變量本身,是一個左值!

這個結(jié)論可能讓你覺得繞,但它是理解后續(xù)所有內(nèi)容的基礎。來看看為什么:

int main() {
    int&& rr1 = 10;
    // rr1是一個右值引用變量,它有自己的名字和地址
    cout << &rr1 << endl;  // 能取地址 → rr1是左值!
    // 所以:rr1可以綁定到左值引用
    int& r6 = rr1;   // ? 沒問題
    // 但不能再次綁定到右值引用(即使它"長著"右值引用的臉)
    // int&& rrx6 = rr1;      // error! rr1是左值
    // 如果你想再轉(zhuǎn)成右值,需要move一下
    int&& rrx6 = move(rr1);   // ?
    return 0;
}

這個"身份轉(zhuǎn)換"是很多初學者踩坑的地方。記住一句話:有名字的右值引用是左值,沒名字的右值(臨時對象、表達式結(jié)果)才是純右值。

五、移動語義:把深拷貝變成"資源掠奪"

5.1 問題從何而來?

來看一個場景:寫一個函數(shù)返回一個大對象,或者向容器中插入一個即將銷毀的對象。

// 自定義的簡化版string類
namespace jys {
    class string {
    public:
        string(const char* str = "")
            : _size(strlen(str)), _capacity(_size) {
            cout << "string(char* str)——構造" << endl;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }
        // 拷貝構造——深拷貝,代價大!
        string(const string& s) : _str(nullptr) {
            cout << "string(const string& s) —— 深拷貝構造" << endl;
            reserve(s._capacity);
            for (auto ch : s) {
                push_back(ch);
            }
        }
        // 拷貝賦值——也是深拷貝
        string& operator=(const string& s) {
            cout << "string& operator=(const string& s) —— 深拷貝賦值" << endl;
            if (this != &s) {
                _str[0] = '\0';
                _size = 0;
                reserve(s._capacity);
                for (auto ch : s) {
                    push_back(ch);
                }
            }
            return *this;
        }
        ~string() { delete[] _str; _str = nullptr; }
        // ... 其他成員函數(shù)省略
    private:
        char* _str = nullptr;
        size_t _size = 0;
        size_t _capacity = 0;
    };
}

現(xiàn)在考慮這樣一個函數(shù):

class Solution {
public:
    jys::string addStrings(jys::string num1, jys::string num2) {
        jys::string str;
        // ... 計算過程,把結(jié)果存到str里 ...
        return str;  // 返回——會發(fā)生什么?
    }
};

在C++98中,return str 會觸發(fā)拷貝構造——把 str 的內(nèi)容完整復制一份到返回值中,然后 str 自己析構。str都要析構了,我們還要老老實實深拷貝它。

同樣的問題也出現(xiàn)在 vector::push_back、list::push_back 等場景——我們把一個臨時對象插入容器,明明原來的對象馬上就沒人用了,還要深拷貝一次,完全是浪費。

C++98對于這個問題束手無策,只能依賴編譯器的返回值優(yōu)化(RVO/NRVO)來救場,但優(yōu)化不是總能生效的。

5.2 移動構造與移動賦值:不要讓到嘴的鴨子飛了

C++11給出了一個徹底的解決方案:移動語義。核心思想很簡單——既然你的生命周期馬上就要結(jié)束,那你的東西自然就歸我了,直接用就行,費那勁深拷貝干啥?

來看看移動構造和移動賦值的寫法:

// 移動構造
string(string&& s) {
    cout << "string(string&& s) —— 移動構造" << endl;
    swap(s);  // 直接把s的內(nèi)部資源"偷"過來
    // s現(xiàn)在指向原本屬于我的空資源
    // s析構時會安全地delete[] nullptr
}
// 移動賦值
string& operator=(string&& s) {
    cout << "string& operator=(string&& s) —— 移動賦值" << endl;
    swap(s);  // 同理,直接交換資源
    return *this;
}

就這么簡單!用 swap 交換資源的所有權——原對象的資源歸我了,我原有的資源給對方(反正對方馬上就要析構了,讓它幫我清理垃圾)。

我們來對比一下效果:

int main() {
    jys::string s1("11111111111111111");
    // 拷貝構造:深拷貝,分配新內(nèi)存 + 逐字符復制
    jys::string s3 = s1;
    // 移動構造:只交換指針,O(1)復雜度!
    jys::string s4 = jys::string("222222222");
    // move(s1)把s1強制轉(zhuǎn)成右值,觸發(fā)移動構造
    // 注意:之后s1就變成空的了,不要再使用
    jys::string s5 = move(s1);
    return 0;
}

輸出:

string(char* str)——構造         ← s1的構造
string(const string& s) —— 深拷貝構造  ← s3 = s1,深拷貝
string(char* str)——構造         ← 匿名對象構造
string(string&& s) —— 移動構造  ← s4,移動走匿名對象的資源
string(string&& s) —— 移動構造  ← s5,移動走s1的資源

移動構造只做了一件事:交換指針。深拷貝的O(n)直接降到了O(1),這對于大對象來說是天壤之別。

5.3 回到最初的例子

還記得那個 addStrings 函數(shù)嗎?在C++11下:

int main() {
    // 妙啊——函數(shù)內(nèi)局部變量str在return時,
    // 編譯器會優(yōu)先選擇移動構造(如果已定義的話)
    jys::string ret = Solution().addStrings(
        "11111111111111111111",
        "222222222222222222222222222"
    );
    cout << ret.c_str() << endl;
    return 0;
}

有了移動構造,即使編譯器不做RVO優(yōu)化,return一個局部變量也是移動而非拷貝——因為局部變量在return時會被自動識別為右值。

再來看一個傳參和插入容器的場景:

int main() {
    jys::string s1("11111111111");
    vector<jys::string> v;
    v.push_back(s1);             // s1是左值 → 拷貝
    v.push_back(move(s1));       // move后是右值 → 移動!
    v.push_back("22222222222");   // 臨時對象是右值 → 移動!
    return 0;
}

小結(jié):移動語義讓C++程序在不改變使用方式的前提下獲得了巨大的性能提升。STL容器(vector、string、map等)在C++11中全面支持了移動語義,很多以前必須深拷貝的場景現(xiàn)在自動變成了廉價的指針交換。

六、萬能引用與引用折疊

6.1 一個神奇的現(xiàn)象

先來看兩行代碼:

template<class T>
void f1(T& x) {}   // 只能接收左值
template<class T>
void f2(T&& x) {}  // 既能接收左值,也能接收右值?!

誒,T&& 不是右值引用嗎?怎么 f2 既能接收左值也能接收右值?

int main() {
    int n = 0;
    f2(n);   // n是左值 → 居然可以!實參類型變成 int&
    f2(0);   // 0是右值 → 實參類型是 int&&
}

這就是C++11中萬能引用(Universal Reference)。規(guī)則是這樣的:

T&& 出現(xiàn)在模板中,且 T 是被推導的類型參數(shù)時,它就不是普通的右值引用,而是萬能引用——它可以根據(jù)傳入的實參類型,自動變成左值引用或右值引用。

具體來說:

  • 傳入左值 → T 推導為 int&T&& 變成 int&
  • 傳入右值 → T 推導為 int,T&& 變成 int&&

6.2 引用折疊——幕后的規(guī)則

萬能引用的背后是一套叫**引用折疊(Reference Collapsing)**的規(guī)則。C++不允許"引用的引用",但當模板推導產(chǎn)生這種情況時,編譯器會按照以下規(guī)則折疊:

折疊前折疊后
T& &T&
T& &&T&
T&& &T&
T&& &&T&&

也就是說,只有兩個都是右值引用時,結(jié)果才是右值引用;其他情況統(tǒng)統(tǒng)是左值引用。

來看具體的例子驗證:

void f1(T& x) {}   // 模板參數(shù)是 T&
void f2(T&& x) {}  // 模板參數(shù)是 T&&
int main() {
    typedef int& lref;
    typedef int&& rref;
    int n = 0;
    // ===== 引用折疊演示 =====
    lref&  r1 = n;   // int& &  → int&
    lref&& r2 = n;   // int& && → int&
    rref&  r3 = n;   // int&& & → int&
    rref&& r4 = 1;   // int&& && → int&&
    // ===== f1(T& x)的實例化情況 =====
    f1<int>(n);          // T=int,void f1(int& x)
    // f1<int>(0);       // error! int& 不能綁右值
    f1<int&>(n);         // T=int&,折疊后 void f1(int& x)
    // f1<int&>(0);      // error! int& 不能綁右值
    f1<int&&>(n);        // T=int&&,折疊后 void f1(int& x)
    // f1<int&&>(0);     // error! int& 不能綁右值
    f1<const int&>(n);   // T=const int&,void f1(const int& x)
    f1<const int&>(0);   // ? const int& 可以綁右值
    // ===== f2(T&& x)的實例化情況 =====
    // f2<int>(n);       // error! int&& 不能綁左值
    f2<int>(0);          // T=int,void f2(int&& x)
    f2<int&>(n);         // T=int&,折疊后 void f2(int& x)
    // f2<int&>(0);      // error! int& 不能綁右值
    // f2<int&&>(n);     // error! int&& 不能綁左值
    f2<int&&>(0);        // T=int&&,void f2(int&& x)
}

6.3 自動推導到底推了什么

對于萬能引用 T&&,模板實參的推導遵循這個規(guī)則:

template<class T>
void Function(T&& t) {
    int a = 0;
    T x = a;       // 這行可以用來"探測"T到底是什么
    cout << &a << endl;
    cout << &x << endl;
}
int main() {
    int a = 0;
    Function(10);            // 右值 → T=int      → void Function(int&& t)
    Function(a);             // 左值 → T=int&     → void Function(int& t)
    Function(std::move(a));  // 右值 → T=int      → void Function(int&& t)
    const int b = 8;
    Function(b);             // const左值 → T=const int& → void Function(const int& t)
    Function(std::move(b));  // const右值 → T=const int  → void Function(const int&& t)
    return 0;
}

注意 const int b 的情況——const 屬性是不會丟的,推導時 const 會被保留在 T 中。如果 Function 內(nèi)部嘗試對 x 做修改操作,當傳入 const int b 時就會編譯報錯。

七、完美轉(zhuǎn)發(fā)——屬性一個都不能少

7.1 為什么要"轉(zhuǎn)發(fā)"?

萬能引用雖然能同時接收左值和右值,但有一個致命的問題——右值引用變量本身是左值。這意味著:

void Fun(int& x)       { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x)      { cout << "右值引用" << endl; }
void Fun(const int&& x){ cout << "const 右值引用" << endl; }
template <class T>
void Function(T&& t) {
    Fun(t);  // 不管傳進來的是啥,t本身是左值,F(xiàn)un(t)永遠調(diào)左值版本!
}
int main() {
    Function(10);            // 傳右值,希望調(diào)Fun(int&&),實際調(diào)了Fun(int&) ??
    Function(std::move(a));  // 同樣被當左值處理
    return 0;
}

傳進來的明明是右值,為什么轉(zhuǎn)發(fā)出去就變成左值了?

問題的根源就是右值引用變量 t 本身是一個左值(我們講過)。t 有了名字,就可以取地址,所以 Fun(t) 永遠會匹配到 Fun(int&)。

我們需要一種機制,能夠保持原始參數(shù)的"左值/右值"屬性不變地轉(zhuǎn)發(fā)給下一個函數(shù)——這就是完美轉(zhuǎn)發(fā)(Perfect Forwarding)。

7.2std::forward——屬性還原術

std::forward<T>(t) 的用法很簡單:

template <class T>
void Function(T&& t) {
    Fun(forward<T>(t));  // 用forward把t的屬性"恢復"回去
}
int main() {
    // 右值 → T=int     → forward<int>(t)  → 右值 → Fun(int&&)
    Function(10);
    int a;
    // 左值 → T=int&    → forward<int&>(t) → 左值 → Fun(int&)
    Function(a);
    // 右值 → T=int     → forward<int>(t)  → 右值 → Fun(int&&)
    Function(std::move(a));
    const int b = 8;
    // const左值 → T=const int& → forward<const int&>(t) → const左值 → Fun(const int&)
    Function(b);
    // const右值 → T=const int  → forward<const int>(t)  → const右值 → Fun(const int&&)
    Function(std::move(b));
    return 0;
}

輸出:

右值引用
左值引用
右值引用
const 左值引用
const 右值引用

完美!傳入什么屬性,傳出什么屬性,一絲一毫都沒有丟失。這就是"完美"的含義。

7.3forward是怎么做到的?

forward 的源碼其實非常精簡(簡化版):

// forward一個左值 → 返回左值引用
template<class T>
T&& forward(typename remove_reference<T>::type& arg) noexcept {
    return static_cast<T&&>(arg);
}
// forward一個右值 → 返回右值引用
template<class T>
T&& forward(typename remove_reference<T>::type&& arg) noexcept {
    return static_cast<T&&>(arg);
}

關鍵在于 static_cast<T&&>(arg) 結(jié)合引用折疊規(guī)則:

調(diào)用方式T的類型T&&折疊后返回
forward<int>(arg)intint&&右值引用
forward<int&>(arg)int&int&左值引用
forward<int&&>(arg)int&&int&&右值引用

T 的類型由調(diào)用者通過模板推導或顯式指定,完美保留原始參數(shù)的值類別屬性。

7.4 實戰(zhàn):在list中用完美轉(zhuǎn)發(fā)優(yōu)化插入

來看看完美轉(zhuǎn)發(fā)在實際容器類中的應用。下面是一個簡化版的list實現(xiàn),它的 push_backinsert 都用了完美轉(zhuǎn)發(fā):

template<class T>
struct list_node {
    T _data;
    list_node<T>* _next;
    list_node<T>* _prev;
    // 萬能引用 + forward → 根據(jù)實參屬性選擇拷貝或移動
    template<class X>
    list_node(X&& data)
        : _data(forward<X>(data))  // 左值→拷貝,右值→移動
        , _next(nullptr)
        , _prev(nullptr)
    {}
};
template<class T>
class list {
public:
    // 萬能引用版本——一個函數(shù)替代了 const T& 和 T&& 兩個重載
    template<class X>
    void push_back(X&& x) {
        insert(end(), forward<X>(x));
    }
    template<class X>
    iterator insert(iterator pos, X&& x) {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* newnode = new Node(forward<X>(x));
        newnode->_next = cur;
        cur->_prev = newnode;
        newnode->_prev = prev;
        prev->_next = newnode;
        ++_size;
        return newnode;
    }
    // ...
};

使用起來非常自然:

int main() {
    jys::list<jys::string> lt;
    jys::string s1("11111111111");
    lt.push_back(s1);              // s1是左值 → 拷貝構造
    jys::string s2("33333333333");
    lt.push_back(move(s2));        // move后是右值 → 移動構造
    lt.push_back("22222222222");   // 字面量是右值 → 移動構造
    return 0;
}

一個函數(shù)模板,同時處理了拷貝和移動兩種情況——這就是完美轉(zhuǎn)發(fā) + 萬能引用的威力。STL中所有容器的 push_back、emplace_backinsert 等方法都是這樣實現(xiàn)的。

八、總結(jié)

本文從C++11的歷史背景出發(fā),沿著一條清晰的邏輯鏈講解了以下核心內(nèi)容:

初始化列表 (initializer_list)
    ↓
左值與右值的本質(zhì)區(qū)別(能取地址嗎?)
    ↓
左值引用 T& vs 右值引用 T&&(能綁啥不能綁啥)
    ↓
移動構造與移動賦值(把深拷貝變"換家")
    ↓
萬能引用 T&& + 引用折疊規(guī)則(模板中的變色龍)
    ↓
完美轉(zhuǎn)發(fā) forward<T>(屬性一個不少地傳下去)

這些特性不是孤立存在的——它們是C++11為提升性能簡化代碼而精心設計的一整套體系:

  • 初始化列表讓語法更統(tǒng)一
  • 右值引用讓"識別臨時對象"成為可能
  • 移動語義讓臨時對象的傳遞不再昂貴
  • 萬能引用+完美轉(zhuǎn)發(fā)讓泛型代碼能寫得更簡潔且保持高效

讀到這里,你對C++11語法的理解是不是更清晰了呢?

到此這篇關于C++11初探:C++發(fā)展史上的里程碑的文章就介紹到這了,更多相關C++11語法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • QT使用SQLite數(shù)據(jù)庫超詳細教程(增刪改查、對大量數(shù)據(jù)快速存儲和更新)

    QT使用SQLite數(shù)據(jù)庫超詳細教程(增刪改查、對大量數(shù)據(jù)快速存儲和更新)

    這篇文章主要給大家介紹了關于QT使用SQLite數(shù)據(jù)庫的相關資料,其中包括增刪改查以及對大量數(shù)據(jù)快速存儲和更新,SQLite是一種嵌入式關系型數(shù)據(jù)庫管理系統(tǒng),它是一個軟件庫,提供了一個自包含、無服務器、零配置的、事務性的SQL數(shù)據(jù)庫引擎,需要的朋友可以參考下
    2024-01-01
  • C語言struct結(jié)構體介紹

    C語言struct結(jié)構體介紹

    C語言中,結(jié)構體類型屬于一種構造類型(其他的構造類型還有:數(shù)組類型,聯(lián)合類型),下面這篇文章主要給大家介紹了關于C語言結(jié)構體(struct)的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-09-09
  • C++/CLI在vs上的安裝和初步使用教程

    C++/CLI在vs上的安裝和初步使用教程

    本文給大家介紹C++/CLI在vs上的安裝和初步使用,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-07-07
  • C++17實現(xiàn)flyweight_factory模板類及使用示例詳解

    C++17實現(xiàn)flyweight_factory模板類及使用示例詳解

    這篇文章主要為大家介紹了C++17實現(xiàn)flyweight_factory模板類及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • C++ delete之靜態(tài)變量問題詳解

    C++ delete之靜態(tài)變量問題詳解

    這篇文章主要為大家詳細介紹了C++delete的一些問題,學習如何動態(tài)創(chuàng)建對象,動態(tài)創(chuàng)建的對象與一般對象的區(qū)別,動態(tài)創(chuàng)建的對象的初始化以及釋放動態(tài)分配的內(nèi)存等知識點,感興趣的朋友可以參考一下
    2021-09-09
  • C++深入講解new與deleted關鍵字的使用

    C++深入講解new與deleted關鍵字的使用

    這篇文章主要介紹了C++中new與deleted關鍵字的使用,new在動態(tài)內(nèi)存中為對象分配空間并返回一個指向該對象的指針;delete接受一個動態(tài)對象的指針, 銷毀該對象, 并釋放與之關聯(lián)的內(nèi)存
    2022-05-05
  • C++實現(xiàn)LeetCode(133.克隆無向圖)

    C++實現(xiàn)LeetCode(133.克隆無向圖)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(133.克隆無向圖),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 基于QT實現(xiàn)本地音樂播放器

    基于QT實現(xiàn)本地音樂播放器

    這篇文章主要為大家詳細介紹了如何基于QT實現(xiàn)簡單的本地音樂播放器,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-03-03
  • 歸并排序的遞歸實現(xiàn)與非遞歸實現(xiàn)代碼

    歸并排序的遞歸實現(xiàn)與非遞歸實現(xiàn)代碼

    以下是對歸并排序的遞歸實現(xiàn)與非遞歸實現(xiàn)代碼進行了詳細的介紹,需要的朋友可以過來參考下
    2013-08-08
  • C C++算法題解LeetCode1408數(shù)組中的字符串匹配

    C C++算法題解LeetCode1408數(shù)組中的字符串匹配

    這篇文章主要為大家介紹了C C++算法題解LeetCode1408數(shù)組中的字符串匹配示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10

最新評論

阆中市| 河东区| 奈曼旗| 平阳县| 聊城市| 扶风县| 阜南县| 白河县| 高雄市| 达州市| 迁西县| 莱西市| 阿尔山市| 教育| 寻乌县| 鄯善县| 泰州市| 罗定市| 右玉县| 沛县| 黄大仙区| 漯河市| 五指山市| 辽中县| 象山县| 沈阳市| 长丰县| 夏邑县| 平泉县| 东源县| 资中县| 武城县| 麻城市| 商河县| 页游| 眉山市| 余姚市| 克东县| 白银市| 浦东新区| 四平市|