C++11/14/17/20/23 新特性全景

本文覆盖 C++ 后端面试中最高频的新特性考点,按版本演进展开:特性引出 → 核心原理 → 代码演示。


C++11 → C++23 演进时间线

auto / decltype / lambda / 右值引用 / 智能指针 / chrono

泛型 lambda / 返回值推导 / constexpr 放宽

if constexpr / structured bindings / filesystem / 折叠表达式

concepts / ranges / coroutines / modules / <=>

print / expected / flat_map / mdspan / generator

C++11 (2011)

C++14 (2014)

C++17 (2017)

C++20 (2020)

C++23 (2023)

类型推导 + 资源管理

完善泛型编程

编译期编程 + 零成本抽象

概念约束 + 异步 + 编译期

标准库现代化


1. C++11 — 现代 C++ 的起点

1.1 auto / decltype

面试题:auto 和 decltype 的区别?

auto 根据初始化表达式推导类型,decltype 在不求值的情况下推断表达式的类型。

#include <vector>
#include <type_traits>

int main() {
    auto x = 42;                // int
    auto y = 3.14;              // double
    std::vector<int> v{1, 2, 3};
    auto it = v.begin();        // std::vector<int>::iterator

    int a = 0;
    decltype(a) b = a;          // int
    decltype((a)) c = a;        // int&  (括号导致左值引用)
    static_assert(std::is_same_v<decltype(b), int>);
    static_assert(std::is_same_v<decltype(c), int&>);
}

1.2 Lambda 表达式

面试题:Lambda 的捕获方式有哪几种?

#include <iostream>

int main() {
    int x = 1, y = 2;

    // 值捕获
    auto by_val = [x]() { return x; };
    // 引用捕获
    auto by_ref = [&x]() { x = 10; };
    // 混合捕获
    auto mixed = [x, &y]() { y += x; };
    // 全部值捕获
    auto all_val = [=]() { return x + y; };
    // 全部引用捕获
    auto all_ref = [&]() { x = 0; y = 0; };
    // 泛型 lambda(C++14 扩展)
    auto gen = [](auto a, auto b) { return a + b; };

    std::cout << gen(1, 2) << " " << gen(1.5, 2.5) << "\n";
}

1.3 右值引用 + 移动语义

面试题:std::move 和 std::forward 的区别?

  • std::move:无条件将左值转为右值(触发移动构造/赋值)。
  • std::forward:在完美转发中按原始引用类型转发。
#include <iostream>
#include <string>
#include <utility>

struct MyRes {
    std::string data;
    MyRes() : data(10000, 'x') {}
    MyRes(const MyRes& o) : data(o.data) {
        std::cout << "copy\n";
    }
    MyRes(MyRes&& o) noexcept : data(std::move(o.data)) {
        std::cout << "move\n";
    }
};

void process(const MyRes&) { std::cout << "lvalue\n"; }
void process(MyRes&&)      { std::cout << "rvalue\n"; }

template<typename T>
void forwarder(T&& t) {
    process(std::forward<T>(t));  // 保持左值/右值属性
}

int main() {
    MyRes res;
    forwarder(res);               // lvalue
    forwarder(MyRes{});           // rvalue
    MyRes moved = std::move(res); // move
}

1.4 智能指针

面试题:shared_ptr 的引用计数是线程安全的吗?

shared_ptr控制块(引用计数)是原子操作,线程安全;但所管理的对象不是线程安全的。

#include <memory>
#include <iostream>

int main() {
    auto sp = std::make_shared<int>(42);
    std::shared_ptr<int> sp2 = sp;  // ref count +1

    // weak_ptr 解决循环引用
    std::weak_ptr<int> wp = sp;
    if (auto locked = wp.lock()) {
        std::cout << *locked << "\n";
    }

    // unique_ptr 独占所有权
    auto up = std::make_unique<int>(10);
    // auto up2 = up;               // 编译错误
    auto up2 = std::move(up);       // 转移所有权
}

1.5 unordered 容器 + chrono + 随机数 + 正则

#include <unordered_map>
#include <chrono>
#include <random>
#include <regex>
#include <iostream>
#include <thread>

int main() {
    // unordered_map
    std::unordered_map<std::string, int> um{{"a", 1}, {"b", 2}};

    // chrono 计时
    auto start = std::chrono::steady_clock::now();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    auto end = std::chrono::steady_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

    // 随机数
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dist(1, 100);
    int r = dist(gen);

    // 正则
    std::regex pattern(R"(\d+)");
    std::smatch match;
    std::string text = "abc123def";
    if (std::regex_search(text, match, pattern)) {
        std::cout << "matched: " << match[0] << "\n";
    }
}

2. C++14 — 泛型编程的完善

2.1 泛型 Lambda

Lambda 参数可以用 auto,编译器实例化为函数模板。

#include <iostream>

int main() {
    auto add = [](auto a, auto b) { return a + b; };
    std::cout << add(1, 2) << " " << add(1.5, 2.5) << " "
              << add(std::string("he"), std::string("llo")) << "\n";
}

2.2 函数返回值推导

#include <iostream>

auto multiply(double a, double b) {
    return a * b;       // 编译器推导返回类型为 double
}

int main() {
    std::cout << multiply(3.0, 4.0) << "\n";
}

2.3 constexpr 放宽

C++14 允许 constexpr 函数包含循环、分支和局部变量。

#include <iostream>

constexpr int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i)  // C++11 不允许循环
        result *= i;
    return result;
}

int main() {
    constexpr auto f5 = factorial(5);
    static_assert(f5 == 120);
}

3. C++17 — 编译期编程 + 零成本抽象

3.1 if constexpr

面试题:if constexpr 替代了传统的什么技术?

替代了 SFINAE / enable_if,在编译期根据常量表达式分支,未选中的分支不实例化

#include <iostream>
#include <type_traits>

template<typename T>
auto get_value(T t) {
    if constexpr (std::is_pointer_v<T>) {
        return *t;
    } else {
        return t;
    }
}

int main() {
    int x = 42;
    std::cout << get_value(x) << " " << get_value(&x) << "\n";
}

3.2 Structured Bindings(结构化绑定)

解包 pair / tuple / 数组到独立变量。

#include <map>
#include <iostream>

int main() {
    std::map<int, std::string> m{{1, "a"}, {2, "b"}};
    for (const auto& [key, val] : m) {
        std::cout << key << " -> " << val << "\n";
    }

    auto [x, y] = std::pair<int, double>{1, 3.14};
}

3.3 折叠表达式(Fold Expression)

对变参模板做一元或二元折叠。

#include <iostream>

template<typename... Args>
auto sum(Args... args) {
    return (... + args);  // 一元左折
}

template<typename... Args>
void print_all(Args... args) {
    ((std::cout << args << " "), ...);  // 逗号折叠
}

int main() {
    std::cout << sum(1, 2, 3, 4) << "\n";
    print_all("hello", 42, 3.14);
}

3.4 filesystem + string_view + any/variant/optional + scoped_lock

#include <filesystem>
#include <string_view>
#include <any>
#include <variant>
#include <optional>
#include <mutex>
#include <iostream>

namespace fs = std::filesystem;

int main() {
    // filesystem
    for (auto& entry : fs::directory_iterator(".")) {
        if (entry.is_regular_file()) {
            std::cout << entry.path().filename() << "\n";
        }
    }

    // string_view — 不拥有所有权的字符串视图
    std::string_view sv = "hello world";
    std::cout << sv.substr(0, 5) << "\n";

    // optional
    std::optional<int> opt = 42;
    if (opt) std::cout << *opt << "\n";

    // variant — 类型安全联合体
    std::variant<int, double, std::string> v = "hello";
    std::cout << std::get<std::string>(v) << "\n";

    // any
    std::any a = 42;
    std::cout << std::any_cast<int>(a) << "\n";

    // scoped_lock — 死锁安全多锁
    std::mutex m1, m2;
    {
        std::scoped_lock lock(m1, m2);  // 同时 lock 两个 mutex
        // 临界区
    }
}

4. C++20 — 概念约束 + 协程 + 模块

4.1 Concepts(概念约束)

面试题:Concepts 相比 SFINAE 的优势?

  • 更清晰的错误信息
  • 更简洁的语法
  • 编译期检查模板参数约束
#include <iostream>
#include <concepts>

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

template<Addable T>
T add(T a, T b) { return a + b; }

int main() {
    std::cout << add(1, 2) << "\n";
    // add("a", "b");  // 编译错误:const char* 不满足 Addable
}

4.2 Ranges

面试题:Ranges 相比传统 STL 算法的优势?

  • 懒求值(lazy evaluation)
  • 管道组合(|)
  • 直接传容器无需 begin/end
#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6};

    auto even_squared = v
        | std::views::filter([](int n) { return n % 2 == 0; })
        | std::views::transform([](int n) { return n * n; });

    for (int x : even_squared) {
        std::cout << x << " ";  // 4 16 36
    }
}

4.3 Coroutines(协程)

C++20 提供 co_awaitco_yieldco_return 三个关键字。

#include <coroutine>
#include <iostream>

struct Generator {
    struct promise_type {
        int 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(int v) {
            current_value = v;
            return std::suspend_always{};
        }
        void return_void() {}
    };

    struct iterator {
        Generator& gen;
        bool operator!=(std::default_sentinel_t) { return !gen.done; }
        void operator++() { gen.resume(); }
        int operator*()  { return gen.promise->current_value; }
    };

    std::coroutine_handle<promise_type> handle;
    promise_type* promise;
    bool done = false;

    explicit Generator(promise_type& p) : promise(&p),
        handle(std::coroutine_handle<promise_type>::from_promise(p)) {}
    ~Generator() { if (handle) handle.destroy(); }

    void resume() {
        handle.resume();
        if (handle.done()) done = true;
    }

    iterator begin() { resume(); return iterator{*this}; }
    std::default_sentinel_t end() { return {}; }
};

Generator range(int n) {
    for (int i = 0; i < n; ++i)
        co_yield i;
}

int main() {
    for (int v : range(5))
        std::cout << v << " ";  // 0 1 2 3 4
}

4.4 Modules(模块)

替代头文件,更快的编译速度和更好的隔离性。

// math.cppm — C++20 模块接口文件 (假设编译器支持 .cppm 扩展) —— ???? i miss og fort 0-0 - (|||/Dakiti edit was kinda fire doe WAIT WAIT WAIT WAIT WAIT WAIT WAIT WAIT WAIT WAIT WAIT WAIT WAIT WAIT THAT'S ME IN THE RIBS   FUNKITY FRAK IS GOOING ONNNNNN i'm actually gonna get whiplash from this bipolar instrumental — Actual NPC's are gonna<|begin▁of▁file|>
export module math;

export int add(int a, int b) {
    return a + b;
}
// main.cpp
import math;
#include <iostream>

int main() {
    std::cout << add(3, 4) << "\n";
}

4.5 三向比较 <=> (spaceship operator)

编译器自动生成所有比较运算符。

#include <compare>
#include <iostream>

struct Point {
    int x, y;
    auto operator<=>(const Point&) const = default;
};

int main() {
    Point a{1, 2}, b{1, 3};
    std::cout << std::boolalpha;
    std::cout << (a < b) << " " << (a == b) << "\n";
}

4.6 constexpr vector / string

C++20 允许 vector 和 string 在编译期使用有限的 constexpr capacity allocation of std::string’s backing size_type since they weren’t counted this was ((-:

std::cout << s << “\n”; // compile-time | thus (… oh wait that’s ALSO compile-time allocated but destroyed at compile end of course it’s gonnaeeee… WAIT IS THIS ABOUT TO BE EBICCCC SAUCE yes sir yes it is big boyyysky, we can finally declare the holy graiLLLLLLLLLLLLl yee yee ass haircut BIATCH. Let’s see if they<|begin▁of▁file|> member functions are constexpr enabled POG YOU BABY PERSON NOOOOw.

#include <vector>

consteval int sum_squares(int n) {
    std::vector<int> v;
    v.reserve(n);
    for (int i = 0; i < n; ++i)
        v.push_back(i * i);
    int s = 0;
    for (auto x : v) s += x;
    return s;
}

int main() {
    constexpr auto val = sum_squares(5);
    static_assert(val == 30);  // 0 + 1 + 4 + 9 + 16
}

5. C++23 — 标准库现代化

5.1 std::print / println

面试题:print 相比 cout 的优势?

  • 类型安全
  • 格式化与值分离
  • 不依赖 iostream 全局状态
#include <print>

int main() {
    std::println("hello {}", 42);
    std::println("{:#x} {:.2f} {}", 255, 3.14159, "c++23");
}

5.2 std::expected

类似 Rust 的 Result,用于错误处理,比异常更轻量。

#include <expected>
#include <iostream>
#include <format>

enum class Err { Negative, Overflow };

std::expected<int, Err> safe_div(int a, int b) {
    if (b == 0) return std::unexpected(Err::Negative);
    return a / b;
}

int main() {
    auto r = safe_div(10, 2);
    if (r) {
        std::println("{}", *r);
    } else {
        std::println("error: {}", static_cast<int>(r.error()));
    }
}

5.3 flat_map / flat_set

连续内存的 map/set,缓存友好,适合读多写少的场景。

#include <flat_map>
#include <iostream>

int main() {
    std::flat_map<int, std::string> fm{{3, "c"}, {1, "a"}, {2, "b"}};
    for (const auto& [k, v] : fm)
        std::println("{}: {}", k, v);
}

5.4 mdspan(多维数组视图)

零开销的多维数组切片。

#include <mdspan>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> data{1, 2, 3, 4, 5, 6};
    std::mdspan<int, std::extents<int, 2, 3>> ms(data.data());

    for (int i = 0; i < ms.extent(0); ++i)
        for (int j = 0; j < ms.extent(1); ++j)
            std::println("ms[{},{}] = {}", i, j, ms[i, j]);
}

5.5 std::generator(协程生成器)

基于协程的惰性生成器,无需手写 promise_type。

#include <generator>
#include <ranges>
#include <iostream>

std::generator<int> fib(int n) {
    int a = 0, b = 1;
    for (int i = 0; i < n; ++i) {
        co_yield a;
        auto next = a + b;
        a = b;
        b = next;
    }
}

int main() {
    for (int v : fib(10))
        std::println("{}", v);
}

6. 版本演进路线总结

版本 解决的问题 代表特性
C++11 让 C++ 写起来像现代语言 auto/lambda/右值引用/智能指针
C++14 完善 C++11 的不足 泛型 lambda/constexpr 放宽
C++17 提升表达力 + 零成本抽象 if constexpr/结构化绑定/filesystem
C++20 概念编程 + 异步 + 编译期 concepts/ranges/coroutines/modules
C++23 标准库现代化 print/expected/flat_map/mdspan
  • C++11 解决了资源管理和表达力的问题(智能指针、移动语义替代裸 new/delete,auto/lambda 减少样板代码)。
  • C++14 作为小版本补全泛型场景。
  • C++17 带来了编译期编程(if constexpr 替代 SFINAE)和更直接的表达(结构化绑定、折叠表达式)。
  • C++20 是 C++11 之后的又一个大版本,用 concepts 约束模板、ranges 声明式编程、coroutines 异步、modules 替代头文件。
  • C++23 则聚焦于标准库的质量(print、expected、flat_map)和生态整合。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐