一、C++11 的发展历史与定位

1.1 C++ 版本演进时间线

C++11 是 C++ 的第二个主要版本,也是自 C++98 以来最重要的更新。在它最终由 ISO 在 2011年8月12日 采纳前,人们曾使用名称 "C++0x",因为它曾被期待在 2010 年之前发布。

C++03 与 C++11 期间花了 8 年时间这是迄今为止最长的版本间隔。从那时起,C++ 有规律地每 3 年 更新一次。

年份 版本 核心特性
1998 C++98 STL(容器与算法)、模板基础
2003 C++03 主要是 Bug 修复
2011 C++11 auto、decltype、lambda、右值引用、智能指针、线程库
2014 C++14 泛型 lambda、变量模板、constexpr 扩展
2017 C++17 constexpr if、结构化绑定、文件系统、并行算法
2020 C++20 概念(Concepts)、协程、模块、Ranges 库
2023 C++23 打印函数(print/println)、std::expected、std::mdspan

1.2 C++11 的核心价值

C++11 引入了大量更改,标准化了既有实践,并改进了对 C++ 程序员可用的抽象。主要贡献包括:

  • 语法简化:autodecltype范围 for、列表初始化

  • 性能提升:右值引用与移动语义、constexpr

  • 现代编程范式支持:lambda、智能指针、线程与内存模型

  • 标准库增强std::arraystd::tuplestd::unordered_map、正则表达式

二、列表初始化(List Initialization)

2.1 C++98 传统的 {} 初始化

在 C++98 中,{} 初始化主要用于数组结构体

struct Point {
    int _x;
    int _y;
};

int main() {
    int array1[] = {1, 2, 3, 4, 5};  // 数组初始化
    int array2[5] = {0};              // 部分初始化,其余为0
    Point p = {1, 2};                 // 结构体初始化(POD 类型)
    return 0;
}

局限性

  • 不支持自定义类对象的 {} 初始化

  • 不支持 vectormap 等容器的 {} 初始化

  • 初始化方式不统一,有 ()={} 多种形式

2.2 C++11 统一列表初始化

C++11 的目标是:一切对象皆可用 {} 初始化,这种初始化方式也称为列表初始化

核心特性

特性 说明
内置类型支持 int x1 = {2};int x2{2};
自定义类型支持 Date d1 = {2025, 1, 1};
可省略 = Point p1{1, 2}; 等价于 Point p1 = {1, 2};
类型转换 本质是构造临时对象 + 拷贝构造,编译器优化为直接构造
容器便利 v.push_back({2025, 1, 1}); 比匿名对象更简洁
#include <iostream>
#include <vector>
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, _month, _day;
};

int main() {
    // C++98 支持的
    int a1[] = {1, 2, 3, 4, 5};
    int a2[5] = {0};
    Point p = {1, 2};

    // C++11 支持的 —— 内置类型
    int x1 = {2};
    int x2{2};          // 省略 =

    // C++11 支持的 —— 自定义类型
    // 本质:用 {2025,1,1} 构造临时对象,再拷贝构造 d1
    // 编译器优化后:合二为一,直接构造 d1
    Date d1 = {2025, 1, 1};

    // d2 引用 {2024,7,25} 构造的临时对象
    const Date& d2 = {2024, 7, 25};

    // 省略 = 的写法
    Point p1{1, 2};
    Date d6{2024, 7, 25};
    const Date& d7{2024, 7, 25};

    // 容器中使用 {} 更有性价比
    vector<Date> v;
    v.push_back(d1);                    // 拷贝构造
    v.push_back(Date(2025, 1, 1));      // 匿名对象
    v.push_back({2025, 1, 1});          // 列表初始化,最简洁!

    return 0;
}

重要说明

  1. 单参数类型转换:C++98 就支持单参数的类型转换,Date d3 = {2025};Date d4 = 2025; 等价

  2. 不能省略 = 的情况:只有 {} 初始化才能省略 =Date d8 2025; 是非法的

  3. 编译器优化Date d1 = {2025, 1, 1}; 理论上需要"构造临时对象 + 拷贝构造",但现代编译器会优化为直接构造

2.3 std::initializer_list

initializer_list - C++ Reference

2.3.1 问题引入

虽然 {} 初始化很方便,但对象容器的初始化仍然不够灵活。比如:

vector<int> v1 = {1, 2, 3};
vector<int> v2 = {1, 2, 3, 4, 5};

如果不用 std::initializer_list,容器需要实现无数个构造函数才能支持任意多个值的初始化。

2.3.2 std::initializer_list 原理

C++11 引入了 std::initializer_list 类:

auto il = {10, 20, 30};  // il 的类型是 initializer_list<int>

底层实现

  • 底层开一个数组,将数据拷贝过来

  • std::initializer_list 内部有两个指针,分别指向数组的开始结束

  • 支持迭代器遍历

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

int main() {
    std::initializer_list<int> mylist;
    mylist = {10, 20, 30};

    cout << sizeof(mylist) << endl;  // 输出 16(两个指针的大小,64位系统)

    // begin 和 end 返回的值是 initializer_list 中存的两个指针
    int i = 0;
    cout << mylist.begin() << endl;  // 数组起始地址
    cout << mylist.end() << endl;    // 数组结束地址
    cout << &i << endl;               // 与上面的地址接近,说明数组存在栈上

    // 容器支持 initializer_list 构造函数
    vector<int> v1({1, 2, 3, 4, 5});   // 直接构造
    vector<int> v2 = {1, 2, 3, 4, 5};   // 构造临时对象 + 拷贝 + 优化为直接构造
    const vector<int>& v3 = {1, 2, 3, 4, 5};

    // pair 的 {} 初始化和 map 的 initializer_list 构造结合
    map<string, string> dict = {
        {"sort", "排序"},
        {"string", "字符串"}
    };

    // initializer_list 版本的赋值
    v1 = {10, 20, 30, 40, 50};

    return 0;
}

2.3.3 模拟实现 vector 的 initializer_list 构造

template<class T>
class vector {
public:
    typedef T* iterator;

    vector(initializer_list<T> l) {
        for (auto e : l)
            push_back(e);
    }

    vector& operator=(initializer_list<value_type> il);

private:
    iterator _start = nullptr;
    iterator _finish = nullptr;
    iterator _endofstorage = nullptr;
};

2.3.4 STL 容器新增接口

// 构造函数
vector(initializer_list<value_type> il, const allocator_type& alloc = allocator_type());
list(initializer_list<value_type> il, const allocator_type& alloc = allocator_type());
map(initializer_list<value_type> il, const key_compare& comp = key_compare(), 
    const allocator_type& alloc = allocator_type());

// 赋值运算符
vector& operator=(initializer_list<value_type> il);
map& operator=(initializer_list<value_type> il);

更多推荐