C++11 发展概述、列表初始化、右值引用与移动语义

1. C++11 发展概述

C++11是 C++ 的第二个主要版本,并且是从 C++98 起的最重要更新。它引入了大量更改,标准化了既有实践,并改进了对C++程序员可用的抽象。在它最终由1S0在 2011年8月12日采纳前,人们曾使用名称“C++0x”,因为它曾被期待在 2010年之前发布。C++03与C++11期间花了8年时间,故而这是迄今为止最长的版本间隔。从那时起,C++有规律地每3年更新一次。
在这里插入图片描述

2. 列表初始化

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};
    Point p = {1, 2};
    return 0;
}

2.2 C++11 统一初始化语法

C++11 引入 {} 初始化语法,旨在提供统一的初始化方式:

// 内置类型
int x1 = {2};
int x2{2};//省略=

// 自定义类型
Date d1 = {2025, 1, 1};
const Date& d2 = {2024, 7, 25};

// 省略等号
Point p1{1, 2};
Date d6{2024, 7, 25};

优势:

  1. 统一所有类型的初始化语法
  2. 避免"最令人烦恼的解析"问题
  3. 防止窄化转换
  4. 容器初始化更方便

2.3 std::initializer_list

C++11 引入 std::initializer_list 支持任意数量元素的初始化:

vector<int> v1 = {1, 2, 3};  // 使用 initializer_list 构造
vector<int> v2{1, 2, 3, 4, 5};

map<string, string> dict = {
    {"sort", "排序"},
    {"string", "字符串"}
};

实现原理是容器提供了接受 initializer_list 的构造函数:

template<class T>
class vector {
public:
    vector(initializer_list<T> il) {
        for (auto e : il)
            push_back(e);
    }
};

3. 右值引用与移动语义

3.1 左值与右值

  • 左值:有持久状态的对象,可以取地址

    • 变量名、解引用指针、字符串字面量
    • 可以出现在赋值符号左侧
  • 右值:临时对象或字面量,不能取地址

    • 字面常量、表达式结果、函数返回值
    • 只能出现在赋值符号右侧
int main() {
    // 左值
    int* p = new int(0);
    int b = 1;
    const int c = b;
    string s("111111");
    
    // 右值
    10;
    x + y;
    fmin(x, y);
    string("11111");
}

3.2 左值引用与右值引用

  • 左值引用:Type&,只能绑定左值,const左值引用可以绑定右值
  • 右值引用:Type&&,只能绑定右值,右值引用可以绑定move后的左值move原理类似强转
int main() {
    int b = 1;
    
    // 左值引用
    int& r1 = b;
    
    // 右值引用
    int&& rr1 = 10;
    
    // const左值引用可以绑定右值
    const int& rx1 = 10;
    
    // 右值引用可以绑定move后的左值
    int&& rrx1 = move(b);
}

3.3 右值引用关键特性

  • 只能绑定到右值(临时对象)

  • 可以延长临时对象的生命周期

  • 主要用途是实现移动语义和完美转发

  • 需要注意的是变量表达式都是左值属性,也就意味着⼀个右值被右值引用绑定后,右值引用变量变量表达式的属性是左值

string&& rref = string("temp");  // 合法
// string&& rref2 = rref;        // 错误!rref本身是左值
string&& rref3 = std::move(rref); // 合法,使用move转换

3.4 移动语义

移动构造和移动赋值允许"窃取"右值资源,避免深拷贝:

class string {
public:
    // 移动构造函数
    string(string&& s) {
        swap(s);  // 直接交换资源
    }
    
    // 移动赋值运算符
    string& operator=(string&& s) {
        swap(s);
        return *this;
    }
};

3.5 完美转发的实现机制

1. 引用折叠规则

当出现引用的引用时,编译器会按照以下规则折叠:

  • T& &T&
  • T& &&T&
  • T&& &T&
  • T&& &&T&&
2. 万能引用

模板参数中的T&&会根据传入实参自动推导:

template <typename T>
void func(T&& arg) {  // 可以是左值引用或右值引用
    // ...
}

int a = 10;
func(a);   // T推导为int&,arg类型为int&
func(10);  // T推导为int,arg类型为int&&
2. 完美转发的使用

结合前面所说,变量表达式都是左值属性,也就意味着一个右值被右值引用绑定后,右值引用变量表达式的属性是左值,也就是说Function函数中t的属性是左值,那么我们把t传递给下一层函数Fun,那么匹配的都是左值引用版本的Fun函数。这里我们想要保持t对象的属性就需要使用完美转发实现。

template <class T>
void Function(T&& t) {
    // 使用forward保持值类别
    Fun(std::forward<T>(t));
}

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

int main() {
    int a = 1;
    Function(a);           // 左值 推导出T为int&
    Function(std::move(a)); // 右值 推导出T为int
    Function(10);          // 右值 推导出T为int
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值