C++11、C++14、C++17、C++20新特性代码示例

该文章已生成可运行项目,

目录

一、C++11 概述

二、C++11 新增特性及代码示例

1. 简化使用

1.1 auto & decltype

1.2 C++11 for 循环(范围 for 循环)

1.3 智能指针

1.4 统一初始化

1.5 nullptr

1.6 constexpr

1.7 explicit

1.8 final & override

1.9 右值引用

1.10 移动语义

1.11 完美转发

2. 并发支持

2.1 内存模型(原子操作)

2.2 线程与锁

2.3 期值(future)

3. 改进对泛型编程的支持

3.1 lambda 表达式

3.2 变参模板

3.3 别名(using)

3.4 tuple

二、C++14 新增特性及代码示例

1. 函数返回值类型推导

2. Lambda 参数支持 auto

3. 变量模板

4. 别名模板

5. constexpr 限制放宽

6. [[deprecated]] 标记

7. 二进制字面量与整形字面量分隔符

8. std::make_unique

9. std::shared_timed_mutex 与 std::shared_lock

10. std::integer_sequence

11. std::exchange

12. std::quoted

三、C++17 新增特性及代码示例

1. 构造函数模板推导

2. 结构化绑定

3. if/switch 语句初始化

4. 内联变量

5. 折叠表达式

6. constexpr Lambda 表达式

7. 嵌套命名空间简化

8. __has_include 预处理表达式

9. Lambda 中用 *this 捕获对象副本

10. 新增 Attribute

11. 字符串转换(std::from_chars/std::to_chars)

12. std::variant

13. std::optional

14. std::any

15. std::apply

16. std::make_from_tuple

17. std::as_const

18. std::string_view

19. std::filesystem

20. std::shared_mutex

四、C++20 新增特性及代码示例

1. 模块(Modules)

2. 范围库(Ranges)

3. 概念库(Concepts)

4. 协程(Coroutines)

5. 并发库(std::jthread)

6. 同步库(std::latch)

7. Lambda 表达式的更新(模板 Lambda)

8. 指定初始化(Designated Initializers)

9. 船型操作符 <=>(三路比较运算符)

10. 范围 for 循环支持初始化语句

11. 非类型模板形参支持字符串

12. C++ 属性符([[likely]]/[[unlikely]])

13. 日历功能(std::chrono::year_month_day)

14. 时区功能(std::chrono::zoned_time)

15. std::span

16. 特性测试宏

17. constexpr 的更新

18. consteval 函数

19. constinit

20. 用 using 引用 enum 类型

21. 格式化库(std::format)

22. 数学常量(std::numbers)

23. std::source_location

24. 位运算(std::rotl/std::rotr)

五、参考链接


一、C++11 概述

C++11 是 C++ 编程语言的重要标准,于 2011 年正式发布(此前曾用名 C++0x)。它是 C++ 自 1998 年标准后的首次重大更新,引入了大量新特性,旨在简化开发、提升性能、增强安全性,并原生支持并发编程,解决了 C++98/03 中的诸多痛点。

二、C++11 新增特性及代码示例

1. 简化使用
1.1 auto & decltype
  • auto:自动推导变量类型,简化声明。
  • decltype:推导表达式类型,常用于模板或类型计算。
#include <iostream>
#include <vector>

int main() {
    // auto:自动推导类型
    auto a = 10;         // 推导为 int
    auto b = 3.14;       // 推导为 double
    std::vector<int> vec = {1, 2, 3};
    auto it = vec.begin();  // 推导为 vector<int>::iterator

    // decltype:推导表达式类型
    int x = 5;
    decltype(x) y = 10;     // y 类型为 int
    decltype(x + b) z = 15.5;  // z 类型为 double(int + double → double)

    return 0;
}
1.2 C++11 for 循环(范围 for 循环)

简化容器遍历,直接迭代元素而非通过索引或迭代器。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};
    
    // 范围 for 循环:遍历容器中所有元素
    for (int num : nums) {
        std::cout << num << " ";  // 输出:1 2 3 4 5
    }
    std::cout << std::endl;

    // 修改元素(需用引用)
    for (int& num : nums) {
        num *= 2;
    }
    for (int num : nums) {
        std::cout << num << " ";  // 输出:2 4 6 8 10
    }

    return 0;
}
1.3 智能指针

自动管理动态内存,避免内存泄漏。

  • unique_ptr:独占所有权,不可复制,只能移动。
#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> ptr1(new int(10));  // 独占指针
    // std::unique_ptr<int> ptr2 = ptr1;  // 错误:不能复制
    std::unique_ptr<int> ptr2 = std::move(ptr1);  // 可以移动(ptr1 变为空)
    
    if (ptr1) std::cout << *ptr1 << std::endl;  // 不执行(ptr1 为空)
    if (ptr2) std::cout << *ptr2 << std::endl;  // 输出:10

    return 0;
}
  • shared_ptr:共享所有权,通过引用计数管理生命周期。
#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr1(new int(20));
    std::cout << "引用计数:" << ptr1.use_count() << std::endl;  // 输出:1

    std::shared_ptr<int> ptr2 = ptr1;  // 共享所有权
    std::cout << "引用计数:" << ptr1.use_count() << std::endl;  // 输出:2

    ptr1.reset();  // 释放 ptr1 的所有权
    std::cout << "引用计数:" << ptr2.use_count() << std::endl;  // 输出:1

    return 0;
}
  • weak_ptr:弱引用,不增加引用计数,用于解决 shared_ptr 的循环引用问题
#include <iostream>
#include <memory>

struct Node {
    std::weak_ptr<Node> next;  // 弱引用,避免循环引用
    ~Node() { std::cout << "Node 被销毁" << std::endl; }
};

int main() {
    std::shared_ptr<Node> node1(new Node());
    std::shared_ptr<Node> node2(new Node());
    
    node1->next = node2;  // weak_ptr 不增加引用计数
    node2->next = node1;

    return 0;  // 离开作用域时,node1 和 node2 引用计数归 0,正常销毁
}
1.4 统一初始化

使用 {} 进行初始化,适用于变量、数组、对象等,避免歧义。

#include <iostream>
#include <vector>
#include <map>

struct Point {
    int x;
    int y;
};

int main() {
    // 基础类型初始化
    int a{10};  // 等价于 int a = 10;

    // 数组初始化
    int arr[]{1, 2, 3, 4};  // 长度自动推导

    // 容器初始化
    std::vector<int> vec{1, 2, 3};  // 直接初始化元素
    std::map<std::string, int> map{{"a", 1}, {"b", 2}};

    // 结构体初始化
    Point p{10, 20};  // 无需构造函数,直接初始化成员

    return 0;
}
1.5 nullptr

专门用于初始化空指针,替代 NULL(避免 NULL 被定义为 0 导致的类型歧义)。

#include <iostream>

void func(int x) {
    std::cout << "int 重载:" << x << std::endl;
}

void func(char* ptr) {
    std::cout << "char* 重载:" << ptr << std::endl;
}

int main() {
    // NULL 会被解析为 0,调用 int 重载
    func(NULL);    // 输出:int 重载:0

    // nullptr 明确为指针类型,调用 char* 重载
    func(nullptr); // 输出:char* 重载:(null)

    return 0;
}
1.6 constexpr

声明常量表达式,在编译期计算,提升性能。

#include <iostream>

// 编译期计算的常量表达式函数
constexpr int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

int main() {
    constexpr int val = factorial(5);  // 编译期计算,结果为 120
    std::cout << val << std::endl;     // 输出:120

    int n = 5;
    // constexpr int val2 = factorial(n);  // 错误:n 是变量,无法在编译期计算

    return 0;
}
1.7 explicit

阻止单参数构造函数的隐式转换,增强代码安全性。

#include <iostream>

class A {
public:
    explicit A(int x) : m_x(x) {}  // explicit 阻止隐式转换
    int getX() const { return m_x; }
private:
    int m_x;
};

void printA(A a) {
    std::cout << a.getX() << std::endl;
}

int main() {
    A a(10);
    printA(a);  // 正确:显式传递 A 对象

    // printA(20);  // 错误:explicit 阻止 int 隐式转换为 A
    printA(A(20));  // 正确:显式构造 A 对象

    return 0;
}
1.8 final & override
  • final:阻止类被继承或函数被重写。
  • override:显式声明重写基类虚函数,避免拼写错误。
#include <iostream>

// final:阻止类被继承
class Base final {
public:
    virtual void func() {
        std::cout << "Base::func()" << std::endl;
    }
};

// class Derived : public Base {};  // 错误:Base 被 final 修饰,不能继承

class Base2 {
public:
    virtual void func() {
        std::cout << "Base2::func()" << std::endl;
    }
    virtual void func2() final {  // final:阻止该函数被重写
        std::cout << "Base2::func2()" << std::endl;
    }
};

class Derived2 : public Base2 {
public:
    void func() override {  // override:明确重写基类函数
        std::cout << "Derived2::func()" << std::endl;
    }
    // void func2() override {}  // 错误:func2 被 final 修饰,不能重写
};

int main() {
    Base2* obj = new Derived2();
    obj->func();  // 输出:Derived2::func()
    obj->func2(); // 输出:Base2::func2()
    delete obj;
    return 0;
}
1.9 右值引用

用 && 表示,绑定到临时对象(右值),用于移动语义和完美转发。

#include <iostream>

// 右值引用函数
void func(int&& x) {
    std::cout << "右值引用:" << x << std::endl;
}

int main() {
    int a = 10;
    // func(a);  // 错误:a 是左值,不能绑定到右值引用
    func(20);    // 正确:20 是右值
    func(a + 5); // 正确:a+5 是右值(临时结果)

    return 0;
}
1.10 移动语义

通过移动构造函数和移动赋值运算符,转移资源所有权,避免不必要的拷贝。

#include <iostream>
#include <vector>

class MyVector {
private:
    int* data;
    size_t size;
public:
    // 构造函数
    MyVector(size_t n) : size(n), data(new int[n]) {
        std::cout << "构造函数:分配 " << n << " 个元素" << std::endl;
    }

    // 移动构造函数(参数为右值引用)
    MyVector(MyVector&& other) noexcept 
        : data(other.data), size(other.size) {
        other.data = nullptr;  // 转移所有权后,原对象资源置空
        other.size = 0;
        std::cout << "移动构造函数:转移资源" << std::endl;
    }

    ~MyVector() {
        if (data) {
            delete[] data;
            std::cout << "析构函数:释放 " << size << " 个元素" << std::endl;
        }
    }
};

int main() {
    MyVector v1(1000);          // 调用构造函数
    MyVector v2 = std::move(v1); // 调用移动构造函数(v1 资源被转移)
    return 0;
}
1.11 完美转发

通过 std::forward 保持参数的左值 / 右值特性,常用于模板函数。

#include <iostream>
#include <utility>

void process(int& x) {
    std::cout << "处理左值:" << x << std::endl;
}

void process(int&& x) {
    std::cout << "处理右值:" << x << std::endl;
}

// 完美转发模板
template <typename T>
void forward_func(T&& arg) {
    process(std::forward<T>(arg));  // 保持 arg 的原始特性
}

int main() {
    int a = 10;
    forward_func(a);       // 传递左值 → 调用 process(int&)
    forward_func(20);      // 传递右值 → 调用 process(int&&)
    forward_func(std::move(a));  // 显式转为右值 → 调用 process(int&&)
    return 0;
}
2. 并发支持
2.1 内存模型(原子操作)

std::atomic 提供原子操作,确保多线程下的数据一致性。

#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> count(0);  // 原子变量,多线程安全

void increment() {
    for (int i = 0; i < 10000; ++i) {
        count++;  // 原子操作,无需加锁
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    std::cout << "最终计数:" << count << std::endl;  // 稳定输出 20000
    return 0;
}
2.2 线程与锁

std::thread 原生支持线程,std::mutex 提供互斥锁。

#include <iostream>
#include <thread>
#include <mutex>

int shared_data = 0;
std::mutex mtx;  // 互斥锁

void increment() {
    for (int i = 0; i < 10000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);  // 自动加锁/解锁
        shared_data++;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    std::cout << "共享数据结果:" << shared_data << std::endl;  // 输出 20000
    return 0;
}
2.3 期值(future)

std::future 和 std::async 用于获取异步操作的结果。

#include <iostream>
#include <future>
#include <chrono>

// 异步执行的函数
int calculate() {
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 模拟耗时计算
    return 100;
}

int main() {
    // 异步启动任务(默认策略:可能创建新线程)
    std::future<int> fut = std::async(calculate);
    
    std::cout << "等待计算结果..." << std::endl;
    int result = fut.get();  // 阻塞等待结果
    
    std::cout << "计算结果:" << result << std::endl;  // 输出:100
    return 0;
}
3. 改进对泛型编程的支持
3.1 lambda 表达式

匿名函数,简化代码(尤其在算法中作为回调)。

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> nums = {3, 1, 4, 1, 5};
    
    // lambda 作为排序谓词(降序)
    std::sort(nums.begin(), nums.end(), [](int a, int b) {
        return a > b;
    });
    
    // lambda 遍历输出
    std::for_each(nums.begin(), nums.end(), [](int num) {
        std::cout << num << " ";  // 输出:5 4 3 1 1
    });

    return 0;
}
3.2 变参模板

支持任意数量的模板参数,用于泛型函数 / 类。

#include <iostream>

// 递归终止函数(处理0个参数)
void print() {
    std::cout << std::endl;
}

// 变参模板函数(处理至少1个参数)
template <typename T, typename... Args>
void print(T first, Args... rest) {
    std::cout << first << " ";
    print(rest...);  // 递归展开参数包
}

int main() {
    print(1, 2.5, "hello", 'a');  // 输出:1 2.5 hello a
    return 0;
}
3.3 别名(using)

替代 typedef,支持模板别名,更灵活。

#include <iostream>
#include <vector>

// 基础类型别名(类似 typedef)
using IntVec = std::vector<int>;

// 模板别名(typedef 无法实现)
template <typename T>
using MyMap = std::map<T, T>;  // 键值类型相同的 map

int main() {
    IntVec vec = {1, 2, 3};  // 等价于 std::vector<int>
    MyMap<int> map;          // 等价于 std::map<int, int>
    map[1] = 10;
    map[2] = 20;
    return 0;
}
3.4 tuple

存储不同类型元素的容器,类似 “匿名结构体”。

#include <iostream>
#include <tuple>
#include <string>

int main() {
    // 创建 tuple(包含 int、double、string)
    std::tuple<int, double, std::string> t(10, 3.14, "hello");
    
    // 获取元素(通过索引,从0开始)
    std::cout << std::get<0>(t) << std::endl;  // 输出:10
    std::cout << std::get<1>(t) << std::endl;  // 输出:3.14
    std::cout << std::get<2>(t) << std::endl;  // 输出:hello

    // 解包 tuple
    int a;
    double b;
    std::string c;
    std::tie(a, b, c) = t;  // a=10, b=3.14, c="hello"

    return 0;
}

二、C++14 新增特性及代码示例

1. 函数返回值类型推导

C++14 允许函数用 auto 作为返回类型,编译器自动推导返回值类型(需保证所有 return 语句类型一致)。

#include <iostream>

// 自动推导返回类型(无需显式声明)
auto add(int a, double b) {
    return a + b;  // 推导为 double
}

auto multiply(double x, int y) {
    return x * y;  // 推导为 double
}

int main() {
    std::cout << add(3, 2.5) << std::endl;    // 输出:5.5
    std::cout << multiply(2.5, 4) << std::endl;  // 输出:10
    return 0;
}
2. Lambda 参数支持 auto

C++14 允许 Lambda 表达式的参数使用 auto,使其成为泛型 Lambda(编译器自动推导参数类型)。

#include <iostream>
#include <string>

int main() {
    // 泛型 Lambda:可接受任意类型参数
    auto sum = [](auto a, auto b) {
        return a + b;
    };

    std::cout << sum(1, 2) << std::endl;          // 输出:3(int+int)
    std::cout << sum(3.5, 2.5) << std::endl;      // 输出:6(double+double)
    std::cout << sum(std::string("Hello "), "World") << std::endl;  // 输出:Hello World
    return 0;
}
3. 变量模板

C++14 允许定义模板变量,为不同类型生成对应的变量实例。

#include <iostream>
#include <cmath>

// 变量模板:不同类型的圆周率
template <typename T>
constexpr T pi = T(3.14159265358979323846);

int main() {
    std::cout << pi<float> << std::endl;   // 单精度 pi
    std::cout << pi<double> << std::endl;  // 双精度 pi
    std::cout << pi<long double> << std::endl;  // 长双精度 pi
    return 0;
}
4. 别名模板

C++14 扩展了别名模板的灵活性,可用于简化复杂模板类型的声明。

#include <iostream>
#include <map>

// 别名模板:键和值类型相同的 map
template <typename T>
using SameTypeMap = std::map<T, T>;

int main() {
    SameTypeMap<int> int_map;    // 等价于 std::map<int, int>
    int_map[1] = 10;
    int_map[2] = 20;

    SameTypeMap<std::string> str_map;  // 等价于 std::map<std::string, std::string>
    str_map["one"] = "1";
    return 0;
}
5. constexpr 限制放宽

C++14 允许 constexpr 函数包含循环、局部变量和条件分支(C++11 仅允许单条 return 语句)。

#include <iostream>

// 编译期计算阶乘(支持循环)
constexpr int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i) {  // C++14 允许 constexpr 函数中使用循环
        result *= i;
    }
    return result;
}

int main() {
    constexpr int val = factorial(5);  // 编译期计算:120
    std::cout << val << std::endl;
    return 0;
}
6. [[deprecated]] 标记

用于标记不推荐使用的函数、变量或类型,编译器会生成警告。

#include <iostream>

// 标记为不推荐使用
[[deprecated("use new_func() instead")]]
void old_func() {
    std::cout << "Old function" << std::endl;
}

void new_func() {
    std::cout << "New function" << std::endl;
}

int main() {
    old_func();  // 编译警告:'old_func' is deprecated: use new_func() instead
    new_func();
    return 0;
}
7. 二进制字面量与整形字面量分隔符
  • 二进制字面量:用 0b 前缀表示(如 0b1010 表示 10)。
  • 整形分隔符:用 ' 分隔数字(如 1'000'000 表示 1000000),增强可读性。
#include <iostream>

int main() {
    int bin = 0b1010'1111;  // 二进制 10101111 = 十进制 175
    int dec = 1'000'000;    // 十进制 1000000
    long long hex = 0x1234'5678'9ABC'DEF0;  // 十六进制,用'分隔

    std::cout << bin << std::endl;   // 输出:175
    std::cout << dec << std::endl;   // 输出:1000000
    return 0;
}
8. std::make_unique

C++14 引入的安全创建 std::unique_ptr 的函数(C++11 仅支持 std::make_shared),避免直接使用 new

#include <iostream>
#include <memory>

struct MyClass {
    int value;
    MyClass(int v) : value(v) {}
};

int main() {
    // 安全创建 unique_ptr(自动推导类型,避免裸 new)
    auto ptr = std::make_unique<MyClass>(42);
    std::cout << ptr->value << std::endl;  // 输出:42
    return 0;
}
9. std::shared_timed_mutex 与 std::shared_lock

支持 “多读单写” 模式:多个读者可同时访问,写者独占访问(带超时机制)。

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>

std::shared_timed_mutex mtx;
int data = 0;

// 读者:共享访问
void reader(int id) {
    std::shared_lock<std::shared_timed_mutex> lock(mtx);  // 共享锁
    std::cout << "Reader " << id << " reads: " << data << std::endl;
}

// 写者:独占访问
void writer() {
    std::unique_lock<std::shared_timed_mutex> lock(mtx);  // 独占锁
    data++;
    std::cout << "Writer updates data to: " << data << std::endl;
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 5; ++i) threads.emplace_back(reader, i);
    threads.emplace_back(writer);
    for (int i = 5; i < 10; ++i) threads.emplace_back(reader, i);

    for (auto& t : threads) t.join();
    return 0;
}
10. std::integer_sequence

生成编译期整数序列,用于模板元编程(如传递连续索引)。

#include <iostream>
#include <utility>  // for integer_sequence

// 打印整数序列(递归展开)
template <int... Args>
void print_sequence(std::integer_sequence<int, Args...>) {
    (std::cout << ... << Args) << std::endl;  // C++17 折叠表达式(此处简化展示)
}

int main() {
    // 生成 0~4 的整数序列
    print_sequence(std::make_integer_sequence<int, 5>());  // 输出:01234
    return 0;
}
11. std::exchange

交换两个值并返回旧值(原子操作,比手动交换更简洁)。

#include <iostream>
#include <utility>  // for exchange

int main() {
    int a = 10, b = 20;
    int old_a = std::exchange(a, b);  // a 变为 20,返回旧值 10

    std::cout << "a = " << a << ", old_a = " << old_a << std::endl;  // a=20, old_a=10
    return 0;
}
12. std::quoted

为字符串添加引号(输出时自动添加,输入时自动去除引号)。

#include <iostream>
#include <iomanip>  // for quoted

int main() {
    std::string s = "Hello World";
    std::cout << std::quoted(s) << std::endl;  // 输出:"Hello World"(带引号)

    std::string input;
    std::cin >> std::quoted(input);  // 输入 "test" 时,input 会被解析为 test(去引号)
    return 0;
}

三、C++17 新增特性及代码示例

1. 构造函数模板推导

编译器可从初始化参数自动推导模板类型,无需显式指定。

#include <iostream>
#include <vector>
#include <pair>

int main() {
    std::vector v = {1, 2, 3};  // 自动推导为 std::vector<int>(C++17 前需写 std::vector<int>)
    std::pair p(10, "hello");   // 自动推导为 std::pair<int, const char*>
    return 0;
}
2. 结构化绑定

分解 tuplepair、数组或结构体的成员,直接绑定到变量。

#include <iostream>
#include <vector>
#include <map>
#include <tuple>

struct Point { int x; int y; };

int main() {
    // 分解 pair
    std::pair<int, std::string> p(1, "one");
    auto [num, str] = p;  // num=1, str="one"

    // 分解 tuple
    std::tuple<float, int, char> t(3.14, 5, 'a');
    auto [f, i, c] = t;  // f=3.14, i=5, c='a'

    // 分解结构体
    Point pt{10, 20};
    auto [x, y] = pt;  // x=10, y=20

    // 遍历 map 时分解键值对
    std::map<int, std::string> map{{1, "a"}, {2, "b"}};
    for (const auto& [key, val] : map) {
        std::cout << key << ":" << val << " ";  // 输出:1:a 2:b
    }
    return 0;
}
3. if/switch 语句初始化

在 if 或 switch 中声明变量,作用域仅限于语句块,避免污染外部作用域。

#include <iostream>
#include <map>

int main() {
    std::map<int, std::string> map{{1, "one"}, {2, "two"}};

    // if 语句中初始化变量(it 仅在 if 块内可见)
    if (auto it = map.find(1); it != map.end()) {
        std::cout << "Found: " << it->second << std::endl;  // 输出:Found: one
    }

    // switch 语句中初始化变量
    switch (int x = 2; x) {  // x 仅在 switch 块内可见
        case 1: std::cout << "1"; break;
        case 2: std::cout << "2"; break;  // 输出:2
    }
    return 0;
}
4. 内联变量

用 inline 修饰变量,允许在头文件中定义全局变量(避免多文件包含时的重复定义错误)。

// header.h(可被多个文件包含)
#ifndef HEADER_H
#define HEADER_H

inline int global_var = 42;  // 内联变量,多文件包含不冲突

#endif

// main.cpp
#include <iostream>
#include "header.h"

int main() {
    std::cout << global_var << std::endl;  // 输出:42
    return 0;
}
5. 折叠表达式

简化变参模板参数包的展开(支持 +*&&|| 等运算符)。

#include <iostream>

// 折叠表达式求和(展开参数包并累加)
template <typename... Args>
auto sum(Args... args) {
    return (... + args);  // 等价于 (((arg1 + arg2) + arg3) + ...)
}

int main() {
    std::cout << sum(1, 2, 3, 4) << std::endl;  // 输出:10
    std::cout << sum(1.5, 2.5, 3) << std::endl;  // 输出:7
    return 0;
}
6. constexpr Lambda 表达式

C++17 允许 Lambda 表达式为 constexpr,可在编译期执行。

#include <iostream>

int main() {
    // constexpr Lambda:编译期计算
    constexpr auto square = [](int x) {
        return x * x;
    };

    constexpr int val = square(5);  // 编译期计算:25
    std::cout << val << std::endl;
    return 0;
}
7. 嵌套命名空间简化

直接用 :: 定义嵌套命名空间(无需多层嵌套 namespace 块)。

#include <iostream>

// 简化的嵌套命名空间(等价于 namespace A { namespace B { namespace C { ... } } })
namespace A::B::C {
    void func() {
        std::cout << "A::B::C::func()" << std::endl;
    }
}

int main() {
    A::B::C::func();  // 输出:A::B::C::func()
    return 0;
}
8. __has_include 预处理表达式

编译期检查头文件是否存在,用于条件编译。

#include <iostream>

// 检查头文件是否存在
#if __has_include(<optional>)
    #include <optional>
    #define HAS_OPTIONAL 1
#else
    #define HAS_OPTIONAL 0
#endif

int main() {
    if (HAS_OPTIONAL) {
        std::cout << "optional 头文件存在" << std::endl;
    } else {
        std::cout << "optional 头文件不存在" << std::endl;
    }
    return 0;
}
9. Lambda 中用 *this 捕获对象副本

捕获当前对象的副本(而非引用),避免 Lambda 生命周期超过对象时的悬垂引用。

#include <iostream>
#include <memory>

struct MyClass {
    int value = 42;
    auto get_lambda() {
        // 捕获 *this(对象副本)
        return [*this]() {
            std::cout << value << std::endl;  // 使用副本的 value
        };
    }
};

int main() {
    auto lambda = std::make_unique<MyClass>()->get_lambda();
    lambda();  // 安全访问副本,输出:42(原对象已销毁,但副本仍有效)
    return 0;
}
10. 新增 Attribute
  • [[nodiscard]]:提示函数返回值不应被忽略(忽略时编译器警告)。
  • [[fallthrough]]:明确标记 switch 中有意省略 break(避免编译器警告)。
#include <iostream>

// [[nodiscard]]:返回值不应被忽略
[[nodiscard]] int calculate() {
    return 42;
}

int main() {
    calculate();  // 警告:忽略 [[nodiscard]] 函数的返回值

    int x = 2;
    switch (x) {
        case 1: std::cout << "1"; break;
        case 2: 
            std::cout << "2";
            [[fallthrough]];  // 明确允许穿透到下一个 case
        case 3: std::cout << "3"; break;  // 输出:23
    }
    return 0;
}
11. 字符串转换(std::from_chars/std::to_chars

高效的字符串与数值转换(无 locale 依赖,性能优于 atoi/to_string)。

#include <iostream>
#include <charconv>  // for from_chars/to_chars
#include <array>

int main() {
    // 字符串转整数
    const char* str = "12345";
    int val;
    auto [ptr, ec] = std::from_chars(str, str + 5, val);
    if (ec == std::errc()) {
        std::cout << "转换结果:" << val << std::endl;  // 输出:12345
    }

    // 整数转字符串
    std::array<char, 10> buf;
    int num = 6789;
    auto [end, ec2] = std::to_chars(buf.data(), buf.data() + buf.size(), num);
    if (ec2 == std::errc()) {
        std::cout << "转换结果:" << std::string(buf.data(), end) << std::endl;  // 输出:6789
    }
    return 0;
}
12. std::variant

类型安全的联合体(可存储多种类型中的一种,需显式指定类型)。

#include <iostream>
#include <variant>  // C++17 新增

int main() {
    std::variant<int, double, std::string> var;

    var = 10;          // 存储 int
    std::cout << std::get<int>(var) << std::endl;  // 输出:10

    var = 3.14;        // 存储 double
    std::cout << std::get<double>(var) << std::endl;  // 输出:3.14

    var = "hello";     // 存储 string
    std::cout << std::get<std::string>(var) << std::endl;  // 输出:hello

    // 检查当前类型
    if (auto* p = std::get_if<std::string>(&var)) {
        std::cout << "当前类型是 string:" << *p << std::endl;
    }
    return 0;
}
13. std::optional

表示 “可能有值或无值” 的对象(用于可能失败的操作返回,避免使用 nullptr 或特殊值)。

#include <iostream>
#include <optional>  // C++17 新增

// 可能返回值或无值(如查找失败)
std::optional<int> find_in_vector(const std::vector<int>& vec, int target) {
    for (int v : vec) {
        if (v == target) return v;
    }
    return std::nullopt;  // 无值
}

int main() {
    std::vector<int> vec = {1, 2, 3, 4};
    
    auto result = find_in_vector(vec, 3);
    if (result.has_value()) {  // 检查是否有值
        std::cout << "找到:" << result.value() << std::endl;  // 输出:找到:3
    }

    auto no_result = find_in_vector(vec, 5);
    if (!no_result) {
        std::cout << "未找到" << std::endl;
    }
    return 0;
}
14. std::any

可存储任意类型的值(类型擦除,需手动指定类型提取)。

#include <iostream>
#include <any>  // C++17 新增
#include <string>

int main() {
    std::any a = 10;          // 存储 int
    std::any b = 3.14;        // 存储 double
    std::any c = std::string("hello");  // 存储 string

    // 提取值(需指定类型)
    std::cout << std::any_cast<int>(a) << std::endl;         // 输出:10
    std::cout << std::any_cast<double>(b) << std::endl;      // 输出:3.14
    std::cout << std::any_cast<std::string>(c) << std::endl; // 输出:hello

    // 检查类型
    if (a.type() == typeid(int)) {
        std::cout << "a 是 int 类型" << std::endl;
    }
    return 0;
}
15. std::apply

将 tuple 的元素作为参数传递给函数。

#include <iostream>
#include <tuple>
#include <utility>  // for apply

int sum(int a, double b, const std::string& c) {
    std::cout << c << ":";
    return a + b;
}

int main() {
    auto t = std::make_tuple(3, 2.5, "sum");
    // 将 tuple 的元素 (3, 2.5, "sum") 作为参数传给 sum
    double result = std::apply(sum, t);
    std::cout << result << std::endl;  // 输出:sum:5.5
    return 0;
}
16. std::make_from_tuple

用 tuple 的元素构造对象(参数传递给构造函数)。

#include <iostream>
#include <tuple>
#include <memory>  // for make_from_tuple

struct Point {
    int x, y;
    Point(int a, int b) : x(a), y(b) {
        std::cout << "Point 构造:(" << x << "," << y << ")" << std::endl;
    }
};

int main() {
    auto t = std::make_tuple(10, 20);  // tuple 元素作为构造参数
    Point p = std::make_from_tuple<Point>(t);  // 用 tuple 构造 Point
    return 0;  // 输出:Point 构造:(10,20)
}
17. std::as_const

将对象转换为 const 引用(避免意外修改,简化代码)。

#include <iostream>
#include <vector>
#include <utility>  // for as_const

int main() {
    std::vector<int> vec = {1, 2, 3};
    
    // 普通遍历(可修改元素)
    for (auto& v : vec) { v *= 2; }
    
    // 用 as_const 转为 const 引用(仅读取)
    for (const auto& v : std::as_const(vec)) {
        std::cout << v << " ";  // 输出:2 4 6
    }
    return 0;
}
18. std::string_view

轻量级字符串视图(引用现有字符串,不复制数据,适合只读场景)。

#include <iostream>
#include <string_view>  // C++17 新增

// 接受 string_view 而非 string,避免复制
void print_str(std::string_view sv) {
    std::cout << sv << std::endl;
}

int main() {
    std::string s = "Hello World";
    print_str(s);  // 传递 string(隐式转换为 string_view)
    print_str("Hello C++17");  // 传递字符串字面量(无复制)
    print_str(s.substr(0, 5));  // 传递子串(无复制)
    return 0;
}
19. std::filesystem

标准化的文件系统操作库(创建目录、遍历文件等)。

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;  // 简化命名空间

int main() {
    // 创建目录
    fs::create_directory("test_dir");
    
    // 遍历目录
    for (const auto& entry : fs::directory_iterator(".")) {
        std::cout << entry.path() << std::endl;  // 输出当前目录下的文件/目录
    }
    
    // 检查路径是否存在
    if (fs::exists("test_dir")) {
        fs::remove("test_dir");  // 删除目录
    }
    return 0;
}
20. std::shared_mutex

C++17 简化的共享互斥锁(替代 C++14 的 std::shared_timed_mutex,无超时机制)。

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>

std::shared_mutex mtx;  // C++17 共享互斥锁
int data = 0;

void reader(int id) {
    std::shared_lock<std::shared_mutex> lock(mtx);  // 共享锁(读)
    std::cout << "Reader " << id << ": " << data << std::endl;
}

void writer() {
    std::unique_lock<std::shared_mutex> lock(mtx);  // 独占锁(写)
    data++;
    std::cout << "Writer: " << data << std::endl;
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 3; ++i) threads.emplace_back(reader, i);
    threads.emplace_back(writer);
    for (int i = 3; i < 6; ++i) threads.emplace_back(reader, i);

    for (auto& t : threads) t.join();
    return 0;
}

四、C++20 新增特性及代码示例

1. 模块(Modules)

替代传统头文件(#include),减少编译时间,控制接口暴露。

// math_module.cppm(模块文件)
export module math;  // 定义模块

export int add(int a, int b) {  // 导出函数
    return a + b;
}

int multiply(int a, int b) {  // 未导出(模块内私有)
    return a * b;
}

// main.cpp
import math;  // 导入模块
#include <iostream>

int main() {
    std::cout << add(2, 3) << std::endl;  // 输出:5(可访问导出函数)
    // std::cout << multiply(2, 3) << std::endl;  // 错误:multiply 未导出
    return 0;
}
2. 范围库(Ranges)

简化算法操作(支持链式调用,自动处理迭代器)。

#include <iostream>
#include <vector>
#include <ranges>  // C++20 范围库
#include <algorithm>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    // 链式操作:筛选偶数 → 乘以 2 → 输出
    auto result = nums | std::views::filter([](int x) { return x % 2 == 0; })
                       | std::views::transform([](int x) { return x * 2; });
    
    for (int x : result) {
        std::cout << x << " ";  // 输出:4 8 12 16
    }
    return 0;
}
3. 概念库(Concepts)

约束模板参数类型(替代繁琐的 enable_if,编译错误更友好)。

#include <iostream>
#include <concepts>  // C++20 概念库

// 约束 T 必须是整数类型(integral 是预定义概念)
template <std::integral T>  // 等价于 requires std::is_integral_v<T>
T square(T x) {
    return x * x;
}

int main() {
    std::cout << square(5) << std::endl;    // 正确:int 是整数类型 → 25
    // std::cout << square(3.14) << std::endl;  // 错误:double 不是整数类型
    return 0;
}
4. 协程(Coroutines)

用于异步操作(可暂停 / 恢复,避免回调地狱),需配合 std::generator(C++20 技术规范)。

#include <iostream>
#include <generator>  // C++20 协程生成器

// 协程:生成从 start 到 end 的序列
std::generator<int> range(int start, int end) {
    for (int i = start; i < end; ++i) {
        co_yield i;  // 暂停并返回当前值
    }
}

int main() {
    for (int x : range(1, 5)) {  // 遍历协程生成的序列
        std::cout << x << " ";  // 输出:1 2 3 4
    }
    return 0;
}
5. 并发库(std::jthread

std::jthread 是 std::thread 的改进版,析构时自动 join(避免忘记 join 导致的程序崩溃)。

#include <iostream>
#include <thread>  // C++20 jthread

void task(int id) {
    std::cout << "Thread " << id << " running" << std::endl;
}

int main() {
    {
        std::jthread t1(task, 1);  // 自动 join(离开作用域时析构)
        std::jthread t2(task, 2);
    }  // t1、t2 在此处自动 join
    std::cout << "All threads joined" << std::endl;
    return 0;
}
6. 同步库(std::latch

协调多个线程(等待所有线程完成后再继续)。

#include <iostream>
#include <thread>
#include <latch>  // C++20 同步库

void worker(int id, std::latch& latch) {
    std::cout << "Worker " << id << " done" << std::endl;
    latch.count_down();  // 计数减 1
}

int main() {
    const int num_workers = 3;
    std::latch latch(num_workers);  // 初始计数为 3

    std::jthread t1(worker, 1, std::ref(latch));
    std::jthread t2(worker, 2, std::ref(latch));
    std::jthread t3(worker, 3, std::ref(latch));

    latch.wait();  // 等待计数减为 0(所有工人完成)
    std::cout << "All workers done" << std::endl;
    return 0;
}
7. Lambda 表达式的更新(模板 Lambda)

C++20 允许 Lambda 显式声明模板参数(更灵活的泛型)。

#include <iostream>
#include <string>

int main() {
    // 模板 Lambda(显式指定模板参数)
    auto print = []<typename T>(const T& value) {
        std::cout << value << std::endl;
    };

    print(42);          // T 推导为 int → 输出:42
    print(3.14);        // T 推导为 double → 输出:3.14
    print("hello");     // T 推导为 const char* → 输出:hello
    return 0;
}
8. 指定初始化(Designated Initializers)

按成员名初始化结构体 / 数组(无需按声明顺序)。

#include <iostream>

struct Point {
    int x;
    int y;
};

int main() {
    // 按成员名初始化结构体(C++20 支持)
    Point p = {.y = 20, .x = 10};  // 不按声明顺序,通过成员名指定
    std::cout << "x=" << p.x << ", y=" << p.y << std::endl;  // 输出:x=10, y=20

    // 数组指定初始化
    int arr[5] = {[2] = 10, [0] = 5};  // 索引 2 为 10,索引 0 为 5
    for (int i = 0; i < 5; ++i) {
        std::cout << arr[i] << " ";  // 输出:5 0 10 0 0
    }
    return 0;
}
9. 船型操作符 <=>(三路比较运算符)

自动生成所有比较运算符(==!=<><=>=)。

#include <iostream>

struct Person {
    std::string name;
    int age;

    // 三路比较:按 age 比较,自动生成所有比较运算符
    auto operator<=>(const Person& other) const {
        return age <=> other.age;
    }
};

int main() {
    Person a{"Alice", 25};
    Person b{"Bob", 30};

    std::cout << (a < b) << std::endl;   // 输出:1(true,25 < 30)
    std::cout << (a > b) << std::endl;   // 输出:0(false)
    std::cout << (a <= b) << std::endl;  // 输出:1(true)
    return 0;
}
10. 范围 for 循环支持初始化语句

在范围 for 循环中声明变量(作用域仅限于循环)。

#include <iostream>
#include <vector>

// 返回一个临时 vector
std::vector<int> get_numbers() {
    return {1, 2, 3, 4};
}

int main() {
    // 循环中初始化变量(避免污染外部作用域)
    for (auto nums = get_numbers(); int x : nums) {
        std::cout << x << " ";  // 输出:1 2 3 4
    }
    return 0;
}
11. 非类型模板形参支持字符串

C++20 允许字符串作为非类型模板参数(需为 constexpr 字符串)。

#include <iostream>

// 字符串作为非类型模板参数
template <const char* Str>
struct Message {
    static void print() {
        std::cout << Str << std::endl;
    }
};

constexpr char hello[] = "Hello C++20";  // constexpr 字符串

int main() {
    Message<hello>::print();  // 输出:Hello C++20
    return 0;
}
12. C++ 属性符([[likely]]/[[unlikely]]

提示编译器分支的可能性,优化代码生成。

#include <iostream>

int main() {
    int x = 1;

    if (x == 1) [[likely]] {  // 提示该分支更可能执行
        std::cout << "x is 1" << std::endl;
    } else [[unlikely]] {     // 提示该分支很少执行
        std::cout << "x is not 1" << std::endl;
    }
    return 0;
}
13. 日历功能(std::chrono::year_month_day

处理日期(年、月、日)的标准化库。

#include <iostream>
#include <chrono>  // C++20 日历库

int main() {
    using namespace std::chrono;

    // 构造日期:2024年3月15日
    year_month_day ymd{year{2024}, month{3}, day{15}};
    
    std::cout << ymd.year() << "-" << ymd.month() << "-" << ymd.day() << std::endl;
    // 输出:2024-Mar-15
    return 0;
}
14. 时区功能(std::chrono::zoned_time

处理带时区的时间(如 UTC 与本地时间转换)。

#include <iostream>
#include <chrono>  // C++20 时区库

int main() {
    using namespace std::chrono;

    // 当前 UTC 时间
    auto now_utc = system_clock::now();
    zoned_time zt{"UTC", now_utc};
    std::cout << "UTC 时间:" << zt << std::endl;

    // 转换为纽约时区
    zoned_time ny_zt{"America/New_York", now_utc};
    std::cout << "纽约时间:" << ny_zt << std::endl;
    return 0;
}
15. std::span

连续序列的视图(类似 string_view,但适用于任意类型数组 / 容器)。

#include <iostream>
#include <span>  // C++20 新增
#include <vector>

// 接受 span 而非容器,支持任意连续序列(数组、vector 等)
void print_span(std::span<int> s) {
    for (int x : s) {
        std::cout << x << " ";
    }
    std::cout << std::endl;
}

int main() {
    int arr[] = {1, 2, 3};
    std::vector<int> vec = {4, 5, 6};

    print_span(arr);    // 传递数组 → 输出:1 2 3
    print_span(vec);    // 传递 vector → 输出:4 5 6
    print_span(vec.data() + 1, 2);  // 传递子序列 → 输出:5 6
    return 0;
}
16. 特性测试宏

编译期检查编译器是否支持特定 C++ 特性(如 __cpp_concepts 检查是否支持 concepts)。

#include <iostream>

int main() {
    #if __cpp_concepts >= 201907L  // 检查 concepts 支持
        std::cout << "支持 C++20 concepts" << std::endl;
    #else
        std::cout << "不支持 C++20 concepts" << std::endl;
    #endif

    #if __cpp_ranges >= 201911L  // 检查 ranges 支持
        std::cout << "支持 C++20 ranges" << std::endl;
    #else
        std::cout << "不支持 C++20 ranges" << std::endl;
    #endif
    return 0;
}
17. constexpr 的更新

C++20 允许 constexpr 函数使用动态内存分配、try/catch 等(进一步扩展编译期计算能力)。

#include <iostream>
#include <vector>

// constexpr 函数中使用动态内存(vector 内部有动态分配)
constexpr int sum(const std::vector<int>& vec) {
    int s = 0;
    for (int v : vec) s += v;
    return s;
}

int main() {
    constexpr std::vector<int> vec = {1, 2, 3, 4};  // constexpr vector(C++20 支持)
    constexpr int total = sum(vec);  // 编译期计算:10
    std::cout << total << std::endl;
    return 0;
}
18. consteval 函数

强制在编译期执行的函数(比 constexpr 更严格,不允许运行期执行)。

#include <iostream>

// consteval:必须在编译期执行
consteval int square(int x) {
    return x * x;
}

int main() {
    constexpr int val1 = square(5);  // 正确:编译期执行 → 25
    // int x = 5;
    // int val2 = square(x);  // 错误:x 是运行期变量,无法编译期执行
    std::cout << val1 << std::endl;
    return 0;
}
19. constinit

确保变量在常量初始化阶段初始化(避免静态初始化顺序问题)。

#include <iostream>

// 常量初始化(编译期完成)
constinit int global_var = 42;

int main() {
    std::cout << global_var << std::endl;  // 输出:42
    return 0;
}
20. 用 using 引用 enum 类型

简化 enum 成员的访问(无需重复 enum 名)。

#include <iostream>

enum class Color {
    Red, Green, Blue
};

int main() {
    using Color::Red;    // 引用 enum 成员
    using Color::Green;

    Color c = Red;       // 无需写 Color::Red
    if (c == Green) {    // 直接使用 Green
        std::cout << "Green" << std::endl;
    } else {
        std::cout << "Red" << std::endl;  // 输出:Red
    }
    return 0;
}
21. 格式化库(std::format

类型安全的字符串格式化(类似 printf,但支持任意类型,编译期检查格式)。

#include <iostream>
#include <format>  // C++20 格式化库

int main() {
    std::string s = std::format("Hello, {}! Age: {}", "Alice", 30);
    std::cout << s << std::endl;  // 输出:Hello, Alice! Age: 30

    // 格式化数字
    std::cout << std::format("Pi: {:.2f}", 3.14159) << std::endl;  // 输出:Pi: 3.14
    return 0;
}
22. 数学常量(std::numbers

标准化的数学常量(如 π、e 等)。

#include <iostream>
#include <numbers>  // C++20 数学常量

int main() {
    std::cout << "π = " << std::numbers::pi << std::endl;      // 输出:3.14159...
    std::cout << "e = " << std::numbers::e << std::endl;       // 输出:2.71828...
    std::cout << "√2 = " << std::numbers::sqrt2 << std::endl;  // 输出:1.41421...
    return 0;
}
23. std::source_location

获取代码位置信息(文件名、行号、函数名),用于日志或调试。

#include <iostream>
#include <source_location>  // C++20 新增

// 获取调用点的位置信息
void log(const std::string& msg, 
         const std::source_location loc = std::source_location::current()) {
    std::cout << "[" << loc.file_name() << ":" << loc.line() << "] " << msg << std::endl;
}

int main() {
    log("程序启动");  // 输出:[main.cpp:11] 程序启动
    return 0;
}

24. 位运算(std::rotl/std::rotr

标准化的位旋转操作(循环左移 / 右移)。

#include <iostream>
#include <bit>  // C++20 位运算库

int main() {
    unsigned int x = 0b1001;  // 二进制 9

    // 循环左移 1 位:1001 → 0011(左移后高位补低位)
    unsigned int rotl = std::rotl(x, 1);
    std::cout << std::hex << rotl << std::endl;  // 输出:3(0b0011)

    // 循环右移 1 位:1001 → 1100(右移后低位补高位)
    unsigned int rotr = std::rotr(x, 1);
    std::cout << rotr << std::endl;  // 输出:c(0b1100)
    return 0;
}

五、参考链接

【C++11、C++14、C++17、C++20新特性整理总结】-CSDN博客

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值