C++ 新特性实战(C++11 / 14 / 17 / 20 / 23)
一、C++11——现代 C++ 的起点
C++11 于 2011 年 发布,被称为"现代 C++ 的起点",引入了大量颠覆性特性。
⭐ 1. auto 类型推导
C++11 之前 vs 之后:
cpp
// ❌ C++11 之前:类型名又臭又长
std::map<std::string, std::vector<int>>::iterator it = m.begin();
// ✅ C++11 之后:auto 自动推导
auto it = m.begin();
// ✅ 范围 for 循环配合 auto
for (const auto& [key, value] : m) { // C++17 结构化绑定
std::cout << key << ": " << value.size() << "\n";
}
auto 推导规则:
| 声明 | 推导结果 | 说明 |
|---|---|---|
auto x = 42; | int | 值拷贝 |
auto& x = val; | int& | 引用 |
const auto& x = val; | const int& | 常量引用 |
auto&& x = 42; | int&& | 万能引用(右值) |
auto&& x = val; | int& | 万能引用(左值) |
⚠️ 注意:
auto会丢弃顶层const和引用,需要时要显式写出。
⭐ 2. 右值引用与移动语义
C++11 之前 vs 之后:
cpp
// ❌ C++11 之前:返回大对象必须拷贝
std::vector<int> CreateData() {
std::vector<int> v(10000, 42);
return v; // 拷贝 10000 个元素(昂贵!)
}
// ✅ C++11 之后:移动语义,窃取资源
std::vector<int> CreateData() {
std::vector<int> v(10000, 42);
return v; // 编译器自动使用移动(甚至 RVO 直接省略)
}
// 显式移动
std::string s1 = "Hello, World!";
std::string s2 = std::move(s1); // s1 的内容被转移到 s2
// s1 现在为空字符串
⭐ 3. Lambda 表达式
C++11 之前 vs 之后:
cpp
std::vector<int> v = {5, 3, 1, 4, 2};
// ❌ C++11 之前:需要定义仿函数
struct Compare {
bool operator()(int a, int b) { return a < b; }
};
std::sort(v.begin(), v.end(), Compare());
// ✅ C++11 之后:Lambda 一行搞定
std::sort(v.begin(), v.end(), [](int a, int b) { return a < b; });
Lambda 语法详解:
[捕获列表](参数列表) mutable -> 返回类型 { 函数体 }
cpp
int x = 10, y = 20;
auto f1 = [x](int a) { return a + x; }; // 值捕获 x
auto f2 = [&x](int a) { x += a; return x; }; // 引用捕获 x
auto f3 = [=]() { return x + y; }; // 值捕获所有
auto f4 = [&]() { x = 1; y = 2; }; // 引用捕获所有
auto f5 = [=, &x]() { x += y; }; // x 引用,其余值捕获
auto f6 = [x]() mutable { x += 10; return x; }; // mutable 允许修改副本
实战:用 Lambda 替代各种场景:
cpp
// 1. 排序
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) { return a.score > b.score; });
// 2. 查找
auto it = std::find_if(v.begin(), v.end(), [](int x) { return x > 100; });
// 3. 遍历
std::for_each(v.begin(), v.end(), [](int x) { std::cout << x << " "; });
// 4. 线程
std::thread t([](int id) {
std::cout << "Thread " << id << "\n";
}, 42);
// 5. 回调
button.OnClick([this]() { HandleClick(); });
⭐ 4. 智能指针
C++11 之前 vs 之后:
cpp
// ❌ C++11 之前:手动管理内存
Widget* w = new Widget();
DoSomething(w); // 如果抛异常 → 内存泄漏
delete w;
// ✅ C++11 之后:智能指针自动管理
auto w = std::make_unique<Widget>(); // C++14
DoSomething(w.get());
// 离开作用域自动释放,即使抛异常也安全
| 智能指针 | 所有权 | 拷贝 | 适用场景 |
|---|---|---|---|
unique_ptr | 独占 | ❌ 仅移动 | 默认首选,单一所有者 |
shared_ptr | 共享 | ✅ 引用计数 | 多个所有者共享 |
weak_ptr | 观察 | — | 打破循环引用 |
⭐ 5. 其他实用特性
cpp
// nullptr(替代 NULL)
int* p = nullptr; // 类型安全
// 范围 for
for (auto& item : container) { /* ... */ }
// enum class(强类型枚举)
enum class Color { Red, Green, Blue };
Color c = Color::Red; // 不会隐式转为 int
// constexpr
constexpr int Square(int x) { return x * x; }
int arr[Square(5)]; // 编译期计算
// override / final
class Base {
virtual void Foo() {}
};
class Derived : public Base {
void Foo() override {} // 编译器检查重写
};
// 初始化列表
std::vector<int> v = {1, 2, 3, 4, 5};
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
// 委托构造
class Foo {
public:
Foo(int x, int y) : x_(x), y_(y) {}
Foo(int x) : Foo(x, 0) {} // 委托给另一个构造函数
Foo() : Foo(0, 0) {}
};
二、C++14——C++11 的完善
C++14 于 2014 年 发布,主要是对 C++11 的修补和增强。
⭐ 核心新特性
cpp
// 1. 泛型 Lambda(auto 参数)
auto add = [](auto a, auto b) { return a + b; };
add(1, 2); // int
add(1.0, 2.0); // double
add("a"s, "b"s); // string
// 2. 返回类型推导
auto Multiply(int a, int b) {
return a * b; // 编译器自动推导返回类型为 int
}
// 3. std::make_unique
auto p = std::make_unique<Widget>(args...); // C++11 遗漏,C++14 补上
// 4. 变量模板
template <typename T>
constexpr T pi = T(3.14159265358979323846);
double area = pi<double> * r * r;
float area_f = pi<float> * r * r;
// 5. 二进制字面量 + 数字分隔符
int binary = 0b1010'1100; // 二进制
long big = 1'000'000'000; // 数字分隔符,增强可读性
// 6. constexpr 函数放宽(允许 if/for/局部变量)
constexpr int Fibonacci(int n) {
if (n <= 1) return n;
int a = 0, b = 1;
for (int i = 2; i <= n; ++i) {
int temp = a + b;
a = b;
b = temp;
}
return b;
}
constexpr int fib10 = Fibonacci(10); // 编译期计算 = 55
// 7. Lambda 初始化捕获(移动捕获)
auto ptr = std::make_unique<int>(42);
auto f = [p = std::move(ptr)]() { return *p; };
// ptr 已被移动,不能再使用
三、C++17——实用性大跃进
C++17 于 2017 年 发布,带来大量日常开发中非常实用的特性。
⭐ 1. 结构化绑定(Structured Bindings)
C++17 之前 vs 之后:
cpp
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
// ❌ C++17 之前
for (const auto& pair : scores) {
std::cout << pair.first << ": " << pair.second << "\n";
}
// ✅ C++17 之后:结构化绑定
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << "\n";
}
// 配合 insert 判断是否成功
auto [iter, inserted] = scores.insert({"Charlie", 92});
if (inserted) {
std::cout << "Inserted: " << iter->first << "\n";
}
// 解构 tuple
auto [x, y, z] = std::make_tuple(1, 2.0, "three"s);
// 解构自定义结构体
struct Point { double x, y; };
auto [px, py] = Point{3.0, 4.0};
⭐ 2. std::optional — 告别空指针
C++17 之前 vs 之后:
cpp
// ❌ C++17 之前:用特殊值/指针表示"无值"
int FindUser(const std::string& name) {
// 返回 -1 表示没找到?还是 0?不清楚!
return -1;
}
// ✅ C++17 之后:optional 明确表达"可能无值"
std::optional<int> FindUser(const std::string& name) {
if (name == "admin") return 42;
return std::nullopt; // 明确表示"没有值"
}
// 使用
auto user = FindUser("admin");
if (user.has_value()) {
std::cout << *user; // 42
}
int id = user.value_or(-1); // 有值返回值,无值返回默认
⭐ 3. std::variant — 类型安全的联合体
cpp
// ❌ C 语言 union:不知道当前存的是什么类型
union Data { int i; double d; char c; };
// ✅ C++17 variant:类型安全,知道当前类型
std::variant<int, double, std::string> v;
v = 42; // 存 int
v = 3.14; // 改为 double
v = "hello"s; // 改为 string
// 获取值
std::string s = std::get<std::string>(v); // "hello"
// 安全获取
if (auto* p = std::get_if<int>(&v)) {
std::cout << "int: " << *p;
}
// 访问者模式(推荐)
std::visit([](auto&& val) {
std::cout << val << "\n";
}, v);
// 实战:错误处理
using Result = std::variant<int, std::string>; // 值或错误信息
Result Divide(int a, int b) {
if (b == 0) return "Division by zero"s;
return a / b;
}
⭐ 4. std::string_view — 零拷贝字符串
cpp
// ❌ C++17 之前:传字符串有性能陷阱
void Log(const std::string& msg); // 传 "hello" 会创建临时 string 对象
Log("hello"); // 隐式构造 std::string → 堆分配!
// ✅ C++17 之后:string_view 零拷贝
void Log(std::string_view msg); // 不拷贝,只是指针+长度
Log("hello"); // 直接引用字面量,零开销
Log(some_string); // 也可以传 string
// string_view 的操作
std::string_view sv = "Hello, World!";
sv.substr(0, 5); // "Hello"(不分配内存!只是调整指针)
sv.remove_prefix(7); // "World!"
sv.find("World"); // 7
⚠️
string_view不拥有数据,必须确保原字符串生命周期更长。
⭐ 5. if / switch 初始化语句
cpp
// ❌ C++17 之前
auto it = m.find(key);
if (it != m.end()) {
// 使用 it
}
// it 泄漏到外部作用域
// ✅ C++17 之后:变量限制在 if 作用域内
if (auto it = m.find(key); it != m.end()) {
std::cout << it->second;
}
// it 在这里已不存在
// switch 也支持
switch (auto val = Compute(); val) {
case 0: /* ... */ break;
case 1: /* ... */ break;
}
⭐ 6. constexpr if — 编译期条件分支
cpp
template <typename T>
std::string ToString(const T& val) {
if constexpr (std::is_integral_v<T>) {
return std::to_string(val);
} else if constexpr (std::is_floating_point_v<T>) {
std::ostringstream oss;
oss << std::fixed << std::setprecision(2) << val;
return oss.str();
} else if constexpr (std::is_same_v<T, std::string>) {
return val;
} else {
static_assert(false, "Unsupported type");
}
}
// 编译器只编译匹配的分支,其他分支直接丢弃
7. 其他实用特性
cpp
// 折叠表达式
template <typename... Args>
auto Sum(Args... args) { return (args + ...); }
Sum(1, 2, 3, 4); // 10
// 类模板参数推导(CTAD)
std::pair p{1, 2.0}; // 自动推导为 pair<int, double>
std::vector v{1, 2, 3}; // vector<int>
std::mutex mtx;
std::lock_guard lock(mtx); // 不需要写 <std::mutex>
// inline 变量(头文件中定义全局变量)
// header.h
inline int globalConfig = 42; // 多个翻译单元共享一份
// 嵌套命名空间
namespace A::B::C {
void Foo() {}
}
// 等价于 namespace A { namespace B { namespace C { ... } } }
// std::filesystem
#include <filesystem>
namespace fs = std::filesystem;
for (auto& entry : fs::directory_iterator(".")) {
std::cout << entry.path() << "\n";
}
// 并行算法
#include <execution>
std::sort(std::execution::par, v.begin(), v.end()); // 并行排序
四、C++20——又一个里程碑
C++20 于 2020 年 发布,被称为"C++11 以来最大的更新"。
⭐ 1. Concepts — 模板的革命
C++20 之前 vs 之后:
cpp
// ❌ C++20 之前:SFINAE 复杂难懂
template <typename T,
typename = std::enable_if_t<std::is_integral_v<T>>>
T Add(T a, T b) { return a + b; }
// 错误信息:一大堆模板替换失败的废话
// ✅ C++20 之后:Concepts 清晰明了
template <std::integral T>
T Add(T a, T b) { return a + b; }
// 错误信息:"int* does not satisfy std::integral"
// 自定义 Concept
template <typename T>
concept Printable = requires(T t, std::ostream& os) {
{ os << t } -> std::same_as<std::ostream&>;
};
// 多种使用方式
template <Printable T>
void Print(const T& val) { std::cout << val; } // 方式1
void Print2(Printable auto const& val) { std::cout << val; } // 方式2(缩写)
template <typename T> requires Printable<T>
void Print3(const T& val) { std::cout << val; } // 方式3
⭐ 2. Ranges — 管道式编程
C++20 之前 vs 之后:
cpp
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// ❌ C++20 之前:冗长的迭代器写法
std::vector<int> result;
std::copy_if(data.begin(), data.end(), std::back_inserter(result),
[](int x) { return x % 2 == 0; });
std::transform(result.begin(), result.end(), result.begin(),
[](int x) { return x * x; });
// ✅ C++20 之后:管道式声明式编程
auto result2 = data
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; })
| std::views::take(3);
for (int x : result2) std::cout << x << " "; // 4 16 36
// Ranges 版算法(直接传容器,不需要 begin/end)
std::ranges::sort(data);
auto it = std::ranges::find(data, 5);
auto cnt = std::ranges::count_if(data, [](int x) { return x > 3; });
⭐ 3. 协程(Coroutines)
cpp
#include <coroutine>
// 生成器示例
Generator<int> Range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i; // 暂停并产出值
}
}
for (int x : Range(0, 10)) {
std::cout << x << " "; // 0 1 2 3 4 5 6 7 8 9
}
// 异步 IO 示例
Task<std::string> FetchData(std::string url) {
auto response = co_await HttpGet(url); // 暂停等待网络请求
co_return response.body(); // 返回结果
}
⭐ 4. 其他重要特性
cpp
// std::jthread(自动 join 的线程)
{
std::jthread jt([](std::stop_token st) {
while (!st.stop_requested()) {
DoWork();
}
});
// 离开作用域自动 join + 自动请求停止
}
// std::format(类似 Python f-string)
std::string s = std::format("Name: {}, Age: {}", "Alice", 30);
std::string hex = std::format("{:#x}", 255); // "0xff"
std::string pi = std::format("{:.2f}", 3.14159); // "3.14"
// 三路比较运算符 <=>(太空船运算符)
struct Point {
int x, y;
auto operator<=>(const Point&) const = default;
// 自动生成 ==, !=, <, >, <=, >=
};
// std::span(连续内存的非拥有视图)
void Process(std::span<int> data) { // 不拷贝,接受 vector/array/C 数组
for (int x : data) std::cout << x;
}
std::vector<int> v = {1, 2, 3};
Process(v); // 自动转换
// consteval(必须编译期执行)
consteval int CompileTimeOnly(int n) { return n * n; }
constexpr int a = CompileTimeOnly(5); // ✅ 编译期
// int b = CompileTimeOnly(runtime_n); // ❌ 编译错误
// constinit(编译期初始化,但不是 const)
constinit int global = 42;
五、C++23——持续进化
C++23 于 2023 年 发布,进一步完善现代 C++ 的实用性。
核心新特性
cpp
// 1. std::expected — 替代异常的错误处理
#include <expected>
std::expected<int, std::string> Divide(int a, int b) {
if (b == 0) return std::unexpected("Division by zero");
return a / b;
}
auto result = Divide(10, 0);
if (result) {
std::cout << *result;
} else {
std::cout << result.error();
}
// 2. std::print / std::println — 替代 cout
#include <print>
std::println("Hello, {}!", "World"); // 自动换行
std::print("x = {}, y = {}\n", 3, 4);
// 3. 推断 this(Deducing this)
struct Widget {
template <typename Self>
auto&& GetValue(this Self&& self) {
return std::forward<Self>(self).value_;
}
// 一个函数同时处理 const/非const/左值/右值
};
// 4. if consteval
constexpr int Foo(int x) {
if consteval {
// 编译期执行的代码
return x * x;
} else {
// 运行期执行的代码
return ComputeAtRuntime(x);
}
}
// 5. std::flat_map / std::flat_set
// 用排序的 vector 替代红黑树,更缓存友好
#include <flat_map>
std::flat_map<std::string, int> fm = {{"a", 1}, {"b", 2}};
// 6. ranges 增强
auto result = v
| std::views::chunk(3) // 分块
| std::views::slide(2) // 滑动窗口
| std::views::zip(other_vec); // 合并
六、版本特性速查表
| 特性 | C++11 | C++14 | C++17 | C++20 | C++23 |
|---|---|---|---|---|---|
auto | ⭐ | 返回类型推导 | — | — | — |
| Lambda | ⭐ 基本 | 泛型 auto | — | 模板 Lambda | — |
| 智能指针 | ⭐ | make_unique | — | — | — |
| 移动语义 | ⭐ T&& / std::move | — | — | — | — |
constexpr | 基本 | 放宽 | if constexpr | consteval | if consteval |
enum class | ⭐ | — | — | — | — |
| 结构化绑定 | — | — | ⭐ | — | — |
optional | — | — | ⭐ | — | — |
variant | — | — | ⭐ | — | — |
string_view | — | — | ⭐ | — | — |
filesystem | — | — | ⭐ | — | — |
| Concepts | — | — | — | ⭐ | — |
| Ranges | — | — | — | ⭐ | 增强 |
| Coroutines | — | — | — | ⭐ | — |
| Modules | — | — | — | ⭐ | — |
format | — | — | — | ⭐ | print |
<=> | — | — | — | ⭐ | — |
span | — | — | — | ⭐ | — |
jthread | — | — | — | ⭐ | — |
expected | — | — | — | — | ⭐ |
七、版本迁移建议
| 场景 | 推荐最低版本 | 说明 |
|---|---|---|
| 新项目 | C++17 | 实用特性丰富,编译器支持成熟 |
| 追求最新 | C++20 | Concepts + Ranges 带来质变 |
| 遗留项目 | C++11 | 至少要用上智能指针和移动语义 |
| 嵌入式 | C++14/17 | 根据编译器支持情况 |
💬 评论