跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++11 核心特性深度解析:列表初始化与右值引用

综述由AI生成C++11 引入了列表初始化和右值引用两大关键特性。列表初始化统一了对象构造方式,支持内置及自定义类型,并优化了临时对象生成。右值引用解决了移动语义的基础问题,通过区分左值和右值,实现了资源的高效转移而非拷贝。本文详细对比了新旧语法差异,解析了生命周期延长机制及参数匹配规则,帮助开发者掌握现代 C++ 的核心编程模式。

Kubernet发布于 2026/3/15更新于 2026/6/820 浏览
C++11 核心特性深度解析:列表初始化与右值引用

C++11 的发展历史

C++11 是 C++98 之后最重要的更新,标准化了既有实践并改进了抽象能力。在 ISO 于 2011 年 8 月正式采纳前,它曾被称为 C++0x。这次更新间隔长达 8 年,此后 C++ 的迭代周期才稳定为每 3 年一次。

文章配图

列表初始化

C++98 传统的{}

在 C++98 中,数组和结构体可以使用花括号进行初始化,但这仅限于特定类型。

struct Point { int _x; int _y; };
int main() {
    int array1[] = { 1, 2, 3, 4, 5 };
    int array2[5] = { 0 };
    Point p = { 1, 2 };
    return 0;
}

C++11 中的{}

C++11 试图统一初始化方式,实现'一切对象皆可用{}初始化',这被称为列表初始化。它不仅支持内置类型,也支持自定义类型。对于自定义类型,本质上是构造临时对象后优化为直接构造。值得注意的是,列表初始化过程中可以省略等号 =。

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

struct P { int a; int b; };

class Date {
public:
    Date(int year = 1, int month = 1, int day = 1) :_year(year), _month(month), _day(day) {
        cout << "Date(int year = 1, int month = 1, int day = 1)" << 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() {
    // C++98 支持
    int a[] = { 1, 2, 3, 4, 5 };
    int a2[5] = { 0 };
    P p = { 1, 2 };

    // C++11 支持
    // 内置类型支持
    int x1 = { 2 };

    // 自定义类型支持
    // 这里本质是用 { 2025, 1, 1 } 构造一个 Date 临时对象
    // 临时对象再去拷贝构造 d1,编译器优化后合二为一变成直接构造
    Date d1 = { 2025, 1, 1 };

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

    // 需要注意的是 C++98 支持单参数时类型转换,也可以不用{}
    Date d3 = { 2025 };
    Date d4 = 2025;

    // 可以省略掉=
    P p1{ 1, 2 };
    int x2{ 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;
}

C++11 中的 std::initializer_list

虽然列表初始化很方便,但在容器初始化场景下仍有局限。如果要用 N 个值构造 vector,传统做法需要重载多个构造函数。C++11 引入了 std::initializer_list,底层开一个数组将数据拷贝过来,内部有两个指针分别指向数组的开始和结束。

STL 容器通过支持 std::initializer_list 的构造函数,实现了任意多个值的 {x1,x2,x3...} 初始化。

#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;

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

    // {}列表中可以有任意多个值
    // 第一个 v1 是直接构造,第二个 v2 是构造临时对象 + 拷贝 + 优化为直接构造
    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;
}

右值引用

左值和右值

左值是表示数据的表达式(如变量名或解引用的指针),通常有持久状态,存储在内存中,我们可以获取它的地址。左值可以出现在赋值符号左边或右边。const 修饰后的左值不可赋值,但可取地址。

int main() {
    int* p = new int(0);
    *p = 10;
    int c = 1;
    const int b = c;
    string s = "hello";
    string s1("world");
    
    cout << &p << endl;
    cout << &b << endl;
    cout << &s << endl;
    cout << &s1 << endl;
    cout << (void*) &s1[0] << endl;
    return 0;
}

文章配图

右值也是表示数据的表达式,通常是字面值常量或表达式求值过程中的临时对象。右值只能出现在赋值符号右边,不能取地址。

// 右值:不能取地址
double x = 1.1, y = 2.2;
// 以下 10、x + y、fmin(x, y)、string("11111") 都是常见的右值
10;
x + y;
fmin(x, y);
string("11111");
// cout << &10 << endl; // 编译错误
// cout << &(x+y) << endl; // 编译错误

文章配图

值得一提的是,lvalue 被解释为 locator value(存储地址的对象),rvalue 被解释为 read value(提供数值但不可寻址)。核心区别在于能否取地址。

左值引用和右值引用

  1. Type& r1 = x; 是左值引用,给左值取别名。Type&& rr1 = y; 是右值引用,给右值取别名。
int main() {
    // 左值:可以取地址
    int* p = new int(0);
    int b = 1;
    const int c = b;
    *p = 10;
    string s("111111");
    s[0] = 'x';
    double x = 1.1, y = 2.2;

    // 左值引用给左值取别名
    int& r1 = b;
    int*& r2 = p;
    int& r3 = *p;
    string& r4 = s;
    char& r5 = s[0];
    return 0;
}

右值引用给右值取别名:

int && rr1 = 10;
int && rr2 = x + y;
double && rr3 = fmin(x, y);
string && rr4 = string("11111");
  1. 左值引用不能直接引用右值,但 const 左值引用可以。
// const 左值引用可以引用右值
const int& rr5 = 10;
const int& rr6 = x + y;
const double& rr7 = fmin(x, y);
const string& rr8 = string("11111");
  1. 右值引用不能直接引用左值,但可以通过 move 函数引用左值。
// 右值引用 move 左值
int&& rrx1 = move(b);
int*&& rrx2 = move(p);
int&& rrx3 = move(*p);
string&& rrx4 = move(s);
char&& rrx5 = move(s[0]);
  1. move 是库里的函数模板,本质是强制类型转换,涉及引用折叠知识。

  2. 变量表达式都是左值属性。这意味着一个右值被右值引用绑定后,该引用变量的属性变为左值。

// b、r1、rr1 都是变量表达式,都是左值
cout << &b << endl;
cout << &r1 << endl;
cout << &rr1 << endl;

// 这里要注意,rr1 的属性是左值,所以不能再被右值引用绑定,除非 move 一下
int& r6 = r1;
// int&& rrx6 = rr1; -- 报错
int&& rrx6 = move(rr1);

引用延长生命周期

右值引用可用于为临时对象延长生命周期。const 左值引用也能延长临时对象生存期,但这些对象无法被修改。

int main() {
    string s1 = "hello";
    // string&& s2 = s1; // 不能右值引用绑定左值
    const string& s2 = s1 + s1; // OK:到 const 的左值引用延长生存期
    // r2 += "Test"; // 错误:不能通过 const 引用修改

    string&& r3 = s1 + s1; // OK:右值引用延长生存期
    r3 += "Test"; // OK:能通过非 const 引用修改
    cout << r3 << '\n';
    return 0;
}

左值和右值的参数匹配

  1. C++98 中,const 左值引用作为参数的函数,实参传递左值和右值都可以匹配。
  2. C++11 以后,分别重载左值引用、const 左值引用、右值引用作为形参的函数,实参会精确匹配对应版本。注意右值引用变量在用于表达式时属性是左值。
void fun1(int& x) { cout << "左值引用重载 func1(int&x)" << endl; }
void fun1(int&& x) { cout << "右值引用重载 func1(int&&x)" << endl; }
void fun1(const int& x) { cout << "左值引用重载 func2(const int&x)" << endl; }

int main() {
    int x = 10;
    const int y = x;
    fun1(x);          // 匹配左值引用
    fun1(10);         // 如果没有 fun1(int&&) 重载则会调用 fun1(const int&)
    fun1(y);          // 匹配 const 左值引用

    // 右值引用变量在用于表达式时是左值
    int&& r = 10;
    fun1(r);          // 匹配左值引用
    fun1(move(r));    // 匹配右值引用
    return 0;
}

文章配图

目录

  1. C++11 的发展历史
  2. 列表初始化
  3. C++98 传统的{}
  4. C++11 中的{}
  5. C++11 中的 std::initializer_list
  6. 右值引用
  7. 左值和右值
  8. 左值引用和右值引用
  9. 引用延长生命周期
  10. 左值和右值的参数匹配
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python sum 函数用法及源码签名误解解析
  • jQuery 核心知识详解:语法、DOM 操作与插件使用
  • IDEA 配置 Maven 详细教程
  • Mac Studio M4 通过 vLLM 部署本地大模型
  • Python 多线程日志错乱:logging.Handler 的并发问题
  • Docker Compose 常用命令详解
  • Linux 进程通信核心原理与实战:匿名管道、命名管道、进程池
  • Java 对象比较详解:equals、hashCode、Comparable 与 Comparator
  • AI 编程实战:自动化生成、低代码开发与算法优化
  • Linux 本地部署 ESPHome 及外网访问方案
  • 位运算实战:判断字符唯一性与查找丢失数字
  • Stable Diffusion WebUI 本地部署指南(AUTOMATIC1111 版)
  • C++ std::vector 使用 std::find 查找指定元素
  • Python 核心面试考点:装饰器、数据结构与版本差异
  • OpenAI o1 模型解析:推理能力突破与人类水平对比
  • 大模型突破对话边界:天工 3.0 与 SkyMusic 评测
  • Python 虚拟环境搭建与 PyCharm 配置实战
  • C++ 网络模块兼容性优化的 7 个关键步骤
  • RISC-V 处理器实战:Verilog 设计与 FPGA 验证流程
  • Windows 环境下 OpenClaw 环境搭建与部署指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online