C++ 新特性实战(C++11 / 14 / 17 / 20 / 23)

✍️ Fer·📅 2026年8月3日·👁 2 次阅读
c++开发
📚 系列:现代 C++ 学习之路

一、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++11C++14C++17C++20C++23
auto返回类型推导
Lambda⭐ 基本泛型 auto模板 Lambda
智能指针make_unique
移动语义T&& / std::move
constexpr基本放宽if constexprconstevalif consteval
enum class
结构化绑定
optional
variant
string_view
filesystem
Concepts
Ranges增强
Coroutines
Modules
formatprint
<=>
span
jthread
expected

七、版本迁移建议

场景推荐最低版本说明
新项目C++17实用特性丰富,编译器支持成熟
追求最新C++20Concepts + Ranges 带来质变
遗留项目C++11至少要用上智能指针和移动语义
嵌入式C++14/17根据编译器支持情况

八、总结

💬 评论

加载评论中...