现代 C++ 演进之旅:从 C++11 到 C++20 的核心特性与最佳实践

现代 C++ 演进之旅:从 C++11 到 C++20 的核心特性与最佳实践

C++ 语言自 2011 年发布 C++11 标准以来,经历了革命性的变化。本文系统梳理了从 C++11 到 C++20 每个版本中最重要的特性、编程习惯和最佳实践,帮助你全面了解现代 C++ 的发展脉络。

C++11:现代 C++ 的开端

C++11 是一次里程碑式的更新,引入了众多改变编程方式的特性。

1. 自动类型推导 (auto)

1
2
3
4
5
6
7
// 旧风格
std::vector<int>::iterator it = vec.begin();

// C++11 新风格
auto it = vec.begin(); // 编译器自动推导类型
auto x = 5; // int
auto name = "Hello"; // const char*

2. 基于范围的 for 循环

1
2
3
4
5
6
7
8
9
// 旧风格
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << std::endl;
}

// C++11 新风格
for (auto& value : vec) { // 注意使用引用避免拷贝
std::cout << value << std::endl;
}

3. 智能指针

1
2
3
4
5
6
7
8
9
10
#include <memory>

// 独占所有权
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();

// 共享所有权
std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>();

// 弱引用(避免循环引用)
std::weak_ptr<MyClass> weak_ptr = ptr2;

4. Lambda 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 旧风格:需要定义独立的函数对象
struct Compare {
bool operator()(int a, int b) const { return a < b; }
};
std::sort(vec.begin(), vec.end(), Compare());

// C++11 新风格:使用 lambda
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return a < b;
});

// 捕获列表示例
int threshold = 42;
std::find_if(vec.begin(), vec.end(), [threshold](int value) {
return value > threshold;
});

5. 右值引用和移动语义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyString {
public:
// 移动构造函数
MyString(MyString&& other) noexcept
: data_(other.data_), size_(other.size_) {
other.data_ = nullptr; // 避免双重释放
other.size_ = 0;
}

// 移动赋值运算符
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
size_ = other.size_;
other.data_ = nullptr;
other.size_ = 0;
}
return *this;
}

private:
char* data_;
size_t size_;
};

6. nullptr

1
2
3
4
5
6
// 旧风格
void foo(int* ptr) {}
foo(NULL); // NULL通常是0,可能调用错误的重载

// C++11 新风格
foo(nullptr); // 明确表示空指针

7. 委托构造函数和继承构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyClass {
public:
// 委托构造函数:一个构造函数调用另一个
MyClass() : MyClass(0, "default") {}
MyClass(int value, const std::string& name)
: value_(value), name_(name) {}

private:
int value_;
std::string name_;
};

// 继承构造函数
class Base {
public:
Base(int value) : value_(value) {}
protected:
int value_;
};

class Derived : public Base {
public:
using Base::Base; // 继承Base的构造函数
// Derived现在有构造函数Derived(int)
};

8. override 和 final 说明符

1
2
3
4
5
6
7
8
9
10
11
class Base {
public:
virtual void foo() const;
virtual void bar() final; // 禁止派生类重写
};

class Derived : public Base {
public:
void foo() const override; // 明确表示重写基类虚函数
// void bar() override; // 错误:bar是final的
};

9. 强类型枚举 (enum class)

1
2
3
4
5
6
7
8
9
// 旧枚举
enum Color { Red, Green, Blue }; // 污染外部作用域
int Red = 5; // 错误:重定义

// C++11 新枚举
enum class Color { Red, Green, Blue }; // 作用域内
Color c = Color::Red; // 必须使用作用域限定
// int i = Color::Red; // 错误:不能隐式转换
int i = static_cast<int>(Color::Red); // 需要显式转换

10. 静态断言 (static_assert)

1
2
3
// 编译期断言检查
static_assert(sizeof(int) == 4, "int must be 4 bytes");
static_assert(sizeof(void*) == 8, "Requires 64-bit platform");

11. 类型别名模板 (using)

1
2
3
4
5
// 比typedef更清晰,特别是对于模板
template<typename T>
using StringMap = std::map<std::string, T>;

StringMap<int> name_to_age; // std::map<std::string, int>

12. 并发支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <thread>
#include <mutex>
#include <future>

// 线程
std::thread t([](){
std::cout << "Hello from thread!" << std::endl;
});
t.join();

// 互斥锁
std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); // RAII方式管理锁

// 异步操作
auto future = std::async(std::launch::async, [](){
return some_heavy_computation();
});
auto result = future.get(); // 获取结果

C++14:改进与优化

C++14 是 C++11 的小幅扩展,主要改进了一些特性的易用性。

1. 函数返回类型推导

1
2
3
4
5
6
7
8
9
// C++14 允许函数使用auto推导返回类型
auto add(int a, int b) {
return a + b; // 返回类型推导为int
}

// 支持引用返回
auto& get_largest(std::vector<int>& vec) {
return *std::max_element(vec.begin(), vec.end());
}

2. 泛型 Lambda

1
2
3
4
5
6
7
8
// Lambda参数可以使用auto
auto print = [](const auto& value) {
std::cout << value << std::endl;
};

print(42); // int
print("Hello"); // const char*
print(3.14); // double

3. 变量模板

1
2
3
4
5
6
template<typename T>
constexpr T pi = T(3.1415926535897932385);

// 使用
float area = pi<float> * radius * radius;
double circumference = 2 * pi<double> * radius;

4. constexpr 函数增强

1
2
3
4
5
6
7
8
9
10
// C++14中constexpr函数可以包含更多语句
constexpr int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}

static_assert(factorial(5) == 120, "Factorial computation error");

C++17:重大更新

C++17 带来了许多重要的新特性,显著提升了代码表达能力和性能。

1. 结构化绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::pair<int, std::string> get_pair() {
return {42, "answer"};
}

// 旧风格
auto p = get_pair();
int value = p.first;
std::string str = p.second;

// C++17 新风格
auto [value, str] = get_pair(); // 直接解包

// 适用于所有结构体、元组和数组
std::map<int, std::string> my_map;
if (auto [iter, success] = my_map.insert({key, value}); success) {
// 插入成功,使用iter
}

2. std::optional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <optional>

// 表示可能不存在的值
std::optional<std::string> find_user(int id) {
if (id == 1) return "Alice";
return std::nullopt; // 没有值
}

// 使用
if (auto user = find_user(1)) {
std::cout << "Found: " << *user << std::endl;
} else {
std::cout << "User not found" << std::endl;
}

// 提供默认值
std::cout << find_user(99).value_or("Unknown") << std::endl;

3. std::variant

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <variant>
#include <string>

// 类型安全的联合体
std::variant<int, std::string, double> value;

value = 42; // 存储int
value = "hello"; // 存储string

// 使用std::visit处理
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>) {
std::cout << "int: " << arg << std::endl;
} else if constexpr (std::is_same_v<T, std::string>) {
std::cout << "string: " << arg << std::endl;
} else if constexpr (std::is_same_v<T, double>) {
std::cout << "double: " << arg << std::endl;
}
}, value);

4. std::string_view

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <string_view>

// 字符串的非拥有视图,避免不必要的拷贝
void process_text(std::string_view text) {
// 可以接受std::string、char*、字符串字面量
std::cout << "Length: " << text.length() << std::endl;
std::cout << "First 5 chars: " << text.substr(0, 5) << std::endl;
}

// 使用
std::string str = "Hello World";
process_text(str); // 无拷贝
process_text("Hello World"); // 无临时string构造

5. 内联变量

1
2
3
4
5
6
7
8
// 头文件中可以定义内联变量
inline constexpr double pi = 3.141592653589793;

// 用于类静态成员
class MyClass {
public:
static inline int counter = 0; // 无需在cpp文件中定义
};

6. if 和 switch 的初始化语句

1
2
3
4
5
6
7
8
9
10
// 将变量作用域限制在条件语句中
if (std::lock_guard lock(mutex); shared_data.ready()) {
// 使用共享数据
} // lock在此自动释放

// switch语句同样适用
switch (auto value = get_value(); value.status()) {
case Status::Ok: /* 处理成功 */ break;
case Status::Error: /* 处理错误 */ break;
}

7. 并行算法

1
2
3
4
5
6
7
8
9
10
11
12
#include <algorithm>
#include <execution>

std::vector<int> data = {5, 3, 2, 6, 1, 4};

// 并行排序
std::sort(std::execution::par, data.begin(), data.end());

// 并行变换
std::transform(std::execution::par,
data.begin(), data.end(), data.begin(),
[](int x) { return x * 2; });

8. 文件系统库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <filesystem>
namespace fs = std::filesystem;

// 遍历目录
for (const auto& entry : fs::directory_iterator(".")) {
if (entry.is_regular_file()) {
std::cout << "File: " << entry.path()
<< " Size: " << entry.file_size() << std::endl;
}
}

// 创建目录
fs::create_directories("/tmp/example/subdir");

// 文件操作
if (fs::exists("file.txt")) {
fs::rename("file.txt", "file_renamed.txt");
}

C++20:重大革新

C++20 是自 C++11 以来最重要的更新,引入了改变编程范式的特性。

1. 概念 (Concepts)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <concepts>

// 定义概念
template<typename T>
concept Arithmetic = std::integral<T> || std::floating_point<T>;

template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as<T>;
};

// 使用概念约束模板
template<Arithmetic T>
T add(T a, T b) {
return a + b;
}

// 更简洁的语法
auto multiply(Arithmetic auto a, Arithmetic auto b) {
return a * b;
}

// 要求类型满足多个概念
template<typename T>
requires Arithmetic<T> && Addable<T>
T compute(T a, T b) {
return add(a, b) * multiply(a, b);
}

2. 范围库 (Ranges)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <ranges>
#include <vector>
#include <iostream>

namespace views = std::views;

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 管道风格的操作
auto result = numbers
| views::filter([](int n) { return n % 2 == 0; }) // 过滤偶数
| views::transform([](int n) { return n * n; }) // 平方
| views::take(3); // 取前3个

// 惰性求值,只有在迭代时才会计算
for (int n : result) {
std::cout << n << ' '; // 输出: 4 16 36
}

// 范围算法
std::vector<int> data = {5, 3, 2, 6, 1, 4};
std::ranges::sort(data); // 比std::sort更简洁

3. 协程 (Coroutines)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <coroutine>
#include <iostream>
#include <vector>

// 生成器协程
template<typename T>
struct Generator {
struct promise_type {
T current_value;
auto get_return_object() { return Generator{this}; }
auto initial_suspend() { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { std::terminate(); }
auto yield_value(T value) {
current_value = value;
return std::suspend_always{};
}
void return_void() {}
};

using Handle = std::coroutine_handle<promise_type>;
Handle coro;

explicit Generator(promise_type* p) : coro(Handle::from_promise(*p)) {}
~Generator() { if (coro) coro.destroy(); }

T value() const { return coro.promise().current_value; }
bool next() {
if (!coro.done()) {
coro.resume();
return !coro.done();
}
return false;
}
};

Generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i; // 挂起并返回值
}
}

// 使用协程
auto gen = range(1, 5);
while (gen.next()) {
std::cout << gen.value() << ' '; // 输出: 1 2 3 4
}

4. 三路比较运算符 (<=>)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <compare>

class Point {
public:
int x;
int y;

// 自动生成所有比较运算符
auto operator<=>(const Point&) const = default;
};

// 使用
Point p1{1, 2}, p2{3, 4};
if (p1 < p2) { // 自动可用
std::cout << "p1 is less than p2" << std::endl;
}

// 自定义三路比较
class CaseInsensitiveString {
public:
std::string value;

std::strong_ordering operator<=>(const CaseInsensitiveString& other) const {
return case_insensitive_compare(value, other.value) <=> 0;
}

bool operator==(const CaseInsensitiveString& other) const {
return case_insensitive_compare(value, other.value) == 0;
}

private:
static int case_insensitive_compare(const std::string& a, const std::string& b) {
// 实现不区分大小写的比较
return 0;
}
};

5. 模块 (Modules)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// math.ixx - 模块接口文件
export module math;

export int add(int a, int b) {
return a + b;
}

export double multiply(double a, double b) {
return a * b;
}

// main.cpp - 使用模块
import math;
import <iostream>;

int main() {
std::cout << add(2, 3) << std::endl; // 5
std::cout << multiply(2.5, 4.0) << std::endl; // 10.0
}

6. std::format

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <format>
#include <iostream>

// 类型安全的字符串格式化
std::string message = std::format("Hello, {}! The answer is {}.", "world", 42);
// message = "Hello, world! The answer is 42."

// 支持格式化规范
double pi = 3.1415926535;
std::string pi_str = std::format("{:.2f}", pi); // "3.14"

// 位置参数
std::string s = std::format("{1} {0}", "world", "Hello"); // "Hello world"

// 使用std::cout直接输出
std::cout << std::format("Value: {:08d}", 42) << std::endl; // "Value: 00000042"

7. std::span

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <span>
#include <vector>
#include <array>

// 表示连续对象序列的视图
void process_data(std::span<const int> data) {
for (int value : data) {
// 处理数据
}
}

// 使用
std::vector<int> vec = {1, 2, 3, 4, 5};
std::array<int, 3> arr = {6, 7, 8};
int c_array[] = {9, 10, 11};

process_data(vec); // 来自vector
process_data(arr); // 来自array
process_data(c_array); // 来自C数组

总结与最佳实践

现代 C++ 提供了丰富的特性来编写更安全、更高效、更易读的代码。以下是一些最佳实践建议:

  1. 优先使用智能指针管理动态内存,避免手动 new/delete
  2. 使用 std::string_view 作为函数参数接收字符串,避免不必要的拷贝
  3. 使用 auto 简化代码,但要在类型明显或重要时保持显式类型声明
  4. 使用范围 for 循环遍历容器,代码更简洁
  5. 使用 nullptr 代替 NULL0 表示空指针
  6. 使用 enum class 代替传统枚举,避免命名污染和隐式转换
  7. 使用 constexpr 将计算移至编译期,提高运行时性能
  8. 使用标准库算法代替手写循环,代码更表达意图
  9. 使用 std::optional 表示可选值,避免特殊值标记
  10. 使用 std::variant 实现类型安全的联合体
  11. 使用概念约束模板参数,使错误信息更友好
  12. 使用范围库编写声明式、可组合的数据处理管道
  13. 使用模块代替头文件,提高编译速度和代码隔离性

避免使用已弃用的特性,如 std::auto_ptrstd::bind1ststd::bind2nd 等,以及 C 风格的字符串函数和内存管理。

通过采用这些现代 C++ 特性,你可以编写出更健壮、更高效、更易维护的代码,充分发挥 C++ 作为系统级编程语言的强大能力。


现代 C++ 演进之旅:从 C++11 到 C++20 的核心特性与最佳实践
https://www.psnow.sbs/2025/09/16/现代-C-演进之旅:从-C-11-到-C-20-的核心特性与最佳实践/
作者
Psnow
发布于
2025年9月16日
许可协议