Chenyj Space
返回博客
·49 分钟

现代 C++ 新特性:从 C++11 到 C++20

C++C++11C++14C++17C++20C/C++

C++11 新特性

1. 自动类型推导(auto)

cpp
// auto 关键字
auto i = 42;            // int
auto d = 3.14;          // double
auto s = "hello";       // const char*
auto str = std::string("hello");  // std::string

// 自动推导复杂类型
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();  // std::vector<int>::iterator

// 函数返回类型推导(C++14)
auto add(int a, int b) {
    return a + b;  // 返回类型推导为 int
}

// decltype - 获取表达式类型
int x = 10;
decltype(x) y = 20;  // y 的类型是 int

// decltype(auto) - 保留引用(C++14)
int& getRef();
decltype(auto) ref = getRef();  // 保留引用

2. 范围 for 循环

cpp
#include <vector>
#include <map>

// 基本用法
std::vector<int> vec = {1, 2, 3, 4, 5};

for (int x : vec) {
    std::cout << x << " ";
}

// 使用引用避免拷贝
for (const auto& x : vec) {
    std::cout << x << " ";
}

// 修改元素
for (auto& x : vec) {
    x *= 2;
}

// 键值对遍历
std::map<std::string, int> map = {{"a", 1}, {"b", 2}};

for (const auto& [key, value] : map) {  // C++17 结构化绑定
    std::cout << key << ": " << value << std::endl;
}

3. Lambda 表达式

cpp
// 基本语法
auto greet = []() {
    std::cout << "Hello!" << std::endl;
};
greet();

// 带参数
auto add = [](int a, int b) {
    return a + b;
};
int sum = add(1, 2);

// 捕获变量
int x = 10;
int y = 20;

auto capture = [x, y]() {  // 值捕获
    return x + y;
};

auto captureRef = [&x, &y]() {  // 引用捕获
    x += 10;
    return x + y;
};

auto captureAll = [=]() {  // 捕获所有变量(值)
    return x + y;
};

auto captureAllRef = [&]() {  // 捕获所有变量(引用)
    x += 10;
    return x + y;
};

// 混合捕获
auto mixed = [x, &y]() {
    y += x;
    return y;
};

// 可变 Lambda
auto mutableLambda = [x]() mutable {
    x += 10;  // 可以修改捕获的值
    return x;
};

// 泛型 Lambda(C++14)
auto generic = [](auto a, auto b) {
    return a + b;
};
int result1 = generic(1, 2);
double result2 = generic(1.5, 2.5);

4. 移动语义

cpp
#include <string>
#include <vector>
#include <utility>

// 左值和右值
int x = 10;        // x 是左值
int&& rref = 10;   // 10 是右值,rref 是右值引用

// 移动构造函数
class MyString {
private:
    char* data;
    size_t size;
    
public:
    // 普通构造函数
    MyString(const char* str) {
        size = strlen(str);
        data = new char[size + 1];
        strcpy(data, str);
    }
    
    // 拷贝构造函数
    MyString(const MyString& other) 
        : size(other.size), data(new char[other.size + 1]) {
        strcpy(data, other.data);
    }
    
    // 移动构造函数
    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;
    }
    
    ~MyString() {
        delete[] data;
    }
};

// std::move - 转换为右值
MyString s1("hello");
MyString s2 = std::move(s1);  // 移动,不是拷贝

// 完美转发
template<typename T>
void wrapper(T&& arg) {
    target(std::forward<T>(arg));  // 保持值类别
}

5. 智能指针

cpp
#include <memory>

// unique_ptr - 独占所有权
std::unique_ptr<int> p1(new int(10));
auto p2 = std::make_unique<int>(20);  // C++14

// 不能拷贝,只能移动
// std::unique_ptr<int> p3 = p1;  // 错误
std::unique_ptr<int> p3 = std::move(p1);

// shared_ptr - 共享所有权
std::shared_ptr<int> sp1(new int(10));
auto sp2 = std::make_shared<int>(20);

std::shared_ptr<int> sp3 = sp1;  // 引用计数 +1

// weak_ptr - 弱引用
std::weak_ptr<int> wp = sp1;
if (auto locked = wp.lock()) {
    // 使用 locked
}

// 自定义删除器
auto deleter = [](int* p) {
    std::cout << "Deleting " << *p << std::endl;
    delete p;
};

std::unique_ptr<int, decltype(deleter)> p4(new int(10), deleter);

6. 右值引用和完美转发

cpp
#include <utility>

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

void process(int&& x) {
    std::cout << "Rvalue: " << x << std::endl;
}

int main() {
    int a = 10;
    process(a);      // 调用 Lvalue 版本
    process(10);     // 调用 Rvalue 版本
    process(std::move(a));  // 调用 Rvalue 版本
}

// 完美转发
template<typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg));
}

// 万能引用
template<typename T>
void universal(T&& arg) {
    // T 可以是左值引用或右值引用
}

// 折叠规则
// T& & → T&
// T& && → T&
// T&& & → T&
// T&& && → T&&

7. 初始化列表

cpp
#include <initializer_list>
#include <vector>

// 统一初始化
int arr[]{1, 2, 3, 4, 5};
std::vector<int> vec{1, 2, 3, 4, 5};

// 类初始化
class Point {
    int x, y;
public:
    Point(int x, int y) : x(x), y(y) {}
};

Point p{1, 2};

// 初始化列表
class MyContainer {
    std::vector<int> data;
    
public:
    MyContainer(std::initializer_list<int> list) 
        : data(list) {}
};

MyContainer container{1, 2, 3, 4, 5};

// 聚合初始化
struct Data {
    int a;
    double b;
    std::string c;
};

Data d{1, 3.14, "hello"};

C++14 新特性

1. 泛型 Lambda

cpp
// 泛型 Lambda
auto add = [](auto a, auto b) {
    return a + b;
};

int result1 = add(1, 2);
double result2 = add(1.5, 2.5);
std::string result3 = add(std::string("hello"), std::string(" world"));

// 模板 Lambda(C++20)
auto genericLambda = []<typename T>(T a, T b) {
    return a + b;
};

2. 返回类型推导

cpp
// 自动推导返回类型
auto factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

// 复杂返回类型
auto getData() {
    return std::make_tuple(1, 3.14, "hello");
}

// 尾置返回类型(C++11)
auto add(int a, int b) -> int {
    return a + b;
}

3. 变量模板

cpp
// 变量模板
template<typename T>
constexpr T pi = T(3.1415926535897932385);

double d = pi<double>;
float f = pi<float>;

// 常量表达式
template<int N>
constexpr int factorial = N * factorial<N - 1>;

template<>
constexpr int factorial<0> = 1;

int result = factorial<5>;  // 编译时计算

4. std::make_unique

cpp
#include <memory>

// C++11
std::unique_ptr<int> p1(new int(10));

// C++14
auto p2 = std::make_unique<int>(10);

// 数组版本
auto p3 = std::make_unique<int[]>(10);

// 优势:
// 1. 更安全(避免裸 new)
// 2. 更高效(单次内存分配)
// 3. 异常安全

C++17 新特性

1. 结构化绑定

cpp
#include <tuple>
#include <map>

// 元组绑定
auto [x, y, z] = std::make_tuple(1, 3.14, "hello");

// 数组绑定
int arr[] = {1, 2, 3};
auto [a, b, c] = arr;

// 结构体绑定
struct Point {
    int x;
    int y;
};

Point p{1, 2};
auto [px, py] = p;

// 键值对绑定
std::map<std::string, int> map = {{"a", 1}, {"b", 2}};
for (const auto& [key, value] : map) {
    std::cout << key << ": " << value << std::endl;
}

// 返回多个值
auto getMinMax(const std::vector<int>& vec) {
    auto [min, max] = std::minmax_element(vec.begin(), vec.end());
    return std::make_pair(*min, *max);
}

2. std::optional

cpp
#include <optional>

// 可能返回值或空
std::optional<int> divide(int a, int b) {
    if (b == 0) {
        return std::nullopt;
    }
    return a / b;
}

// 使用
auto result = divide(10, 2);
if (result) {
    std::cout << "Result: " << *result << std::endl;
}

// 默认值
int value = result.value_or(0);

// std::optional 作为成员
class Config {
    std::optional<std::string> name;
    std::optional<int> timeout;
    
public:
    void setName(const std::string& n) { name = n; }
    void setTimeout(int t) { timeout = t; }
    
    std::string getName() const { 
        return name.value_or("default"); 
    }
};

3. std::variant

cpp
#include <variant>
#include <string>

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

// 赋值
value = 42;
value = 3.14;
value = "hello";

// 访问
std::visit([](auto&& arg) {
    std::cout << arg << std::endl;
}, value);

// 获取值
int i = std::get<int>(value);  // 如果类型不匹配,抛异常

// 安全获取
if (auto* p = std::get_if<int>(&value)) {
    std::cout << "Int: " << *p << std::endl;
}

// 访问者模式
struct Visitor {
    void operator()(int i) const {
        std::cout << "Int: " << i << std::endl;
    }
    
    void operator()(double d) const {
        std::cout << "Double: " << d << std::endl;
    }
    
    void operator()(const std::string& s) const {
        std::cout << "String: " << s << std::endl;
    }
};

std::visit(Visitor{}, value);

4. std::any

cpp
#include <any>

// 任意类型
std::any value = 42;
value = 3.14;
value = std::string("hello");

// 获取值
int i = std::any_cast<int>(value);  // 类型不匹配抛异常

// 安全获取
if (auto* p = std::any_cast<int>(&value)) {
    std::cout << "Int: " << *p << std::endl;
}

// 检查类型
if (value.type() == typeid(int)) {
    std::cout << "Is int" << std::endl;
}

// 应用场景
class AnyContainer {
    std::map<std::string, std::any> data;
    
public:
    template<typename T>
    void set(const std::string& key, T value) {
        data[key] = value;
    }
    
    template<typename T>
    T get(const std::string& key) const {
        return std::any_cast<T>(data.at(key));
    }
};

5. if constexpr

cpp
// 编译时条件判断
template<typename T>
auto get_value(T t) {
    if constexpr (std::is_integral_v<T>) {
        return t * 2;
    } else if constexpr (std::is_floating_point_v<T>) {
        return t * 1.5;
    } else {
        return t;
    }
}

// SFINAE 的替代
template<typename T>
void process(T value) {
    if constexpr (std::is_arithmetic_v<T>) {
        std::cout << "Arithmetic: " << value << std::endl;
    } else {
        std::cout << "Non-arithmetic" << std::endl;
    }
}

// 递归展开
template<typename T, typename... Args>
void print(T first, Args... args) {
    std::cout << first;
    if constexpr (sizeof...(args) > 0) {
        std::cout << ", ";
        print(args...);
    } else {
        std::cout << std::endl;
    }
}

6. 折叠表达式

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

// 二元折叠
template<typename... Args>
auto sum_with_init(Args... args) {
    return (args + ... + 0);  // 带初始值
}

// 逻辑折叠
template<typename... Args>
bool all_true(Args... args) {
    return (args && ...);
}

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

// 使用示例
int result = sum(1, 2, 3, 4, 5);  // 15
bool all = all_true(true, true, false);  // false
print_all(1, 2.5, "hello", 'c');

C++20 新特性

1. 概念(Concepts)

cpp
#include <concepts>

// 定义概念
template&lt;typename T>
concept Numeric = std::integral&lt;T> || std::floating_point&lt;T>;

template&lt;typename T>
concept Hashable = requires(T t) {
    { std::hash&lt;T>{}(t) } -> std::convertible_to&lt;std::size_t>;
};

// 使用概念
template&lt;Numeric T>
T add(T a, T b) {
    return a + b;
}

// 约束 auto
Numeric auto value = 42;

// requires 表达式
template&lt;typename T>
concept Printable = requires(T t) {
    { std::cout << t } -> std::same_as&lt;std::ostream&>;
};

// 组合概念
template&lt;typename T>
concept SignedNumeric = Numeric&lt;T> && std::is_signed_v&lt;T>;

// 标准库概念
template&lt;std::default_initializable T>
T create() {
    return T();
}

2. 范围(Ranges)

cpp
#include <ranges>
#include <vector>

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

// 过滤偶数
auto even = vec | std::views::filter([](int x) { 
    return x % 2 == 0; 
});

// 转换
auto doubled = vec | std::views::transform([](int x) { 
    return x * 2; 
});

// 组合
auto result = vec 
    | std::views::filter([](int x) { return x > 3; })
    | std::views::transform([](int x) { return x * 2; })
    | std::views::take(3);

// 范围算法
std::ranges::sort(vec);
auto it = std::ranges::find(vec, 5);

// 生成器
auto iota = std::views::iota(1, 10);  // 1, 2, ..., 9
auto repeat = std::views::repeat(42);  // 42, 42, ...

3. 协程(Coroutines)

cpp
#include <coroutine>
#include <iostream>

// 协程任务类型
template<typename T>
struct Task {
    struct promise_type {
        T value;
        
        Task get_return_object() {
            return Task{
                std::coroutine_handle<promise_type>::from_promise(*this)
            };
        }
        
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        
        void return_value(T v) {
            value = v;
        }
        
        void unhandled_exception() {
            std::terminate();
        }
    };
    
    std::coroutine_handle<promise_type> handle;
    
    T get() {
        return handle.promise().value;
    }
};

// 协程函数
Task<int> compute(int x) {
    co_await std::suspend_always{};
    co_return x * x;
}

// 异步生成器
template<typename T>
struct Generator {
    struct promise_type {
        T value;
        
        Generator get_return_object() {
            return Generator{
                std::coroutine_handle<promise_type>::from_promise(*this)
            };
        }
        
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        
        std::suspend_always yield_value(T v) {
            value = v;
            return {};
        }
        
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };
    
    std::coroutine_handle<promise_type> handle;
    
    // 迭代器
    struct iterator {
        std::coroutine_handle<promise_type> handle;
        
        iterator& operator++() {
            handle.resume();
            return *this;
        }
        
        T operator*() const {
            return handle.promise().value;
        }
        
        bool operator==(std::default_sentinel_t) const {
            return handle.done();
        }
    };
    
    iterator begin() {
        return {handle};
    }
    
    std::default_sentinel_t end() {
        return {};
    }
};

Generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;
        auto temp = a;
        a = b;
        b = temp + b;
    }
}

4. 三向比较运算符(Spaceship Operator)

cpp
#include <compare>

// 自动生成比较运算符
struct Point {
    int x, y;
    
    auto operator==(const Point&) const = default;
    auto operator<=>(const Point&) const = default;
};

// 使用
Point p1{1, 2}, p2{1, 3};
auto result = p1 <=> p2;

if (result < 0) {
    std::cout << "p1 < p2" << std::endl;
} else if (result > 0) {
    std::cout << "p1 > p2" << std::endl;
} else {
    std::cout << "p1 == p2" << std::endl;
}

// 比较类别
// std::strong_ordering - 全序,可替换
// std::weak_ordering - 全序,不可替换
// std::partial_ordering - 偏序(如浮点数)

5. 模块(Modules)

cpp
// math.cppm (模块接口)
export module math;

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

export class Calculator {
public:
    int multiply(int a, int b) {
        return a * b;
    }
};

// main.cpp
import math;
import <iostream>;

int main() {
    std::cout << add(1, 2) << std::endl;
    
    Calculator calc;
    std::cout << calc.multiply(3, 4) << std::endl;
    
    return 0;
}

// 优势:
// 1. 更快的编译速度
// 2. 更好的封装
// 3. 避免宏泄漏
// 4. 消除头文件问题

6. std::format

cpp
#include <format>
#include <iostream>

// 格式化字符串
std::string s1 = std::format("Hello, {}!", "world");
std::string s2 = std::format("The answer is {}", 42);
std::string s3 = std::format("{:.2f}", 3.14159);

// 命名参数
std::string s4 = std::format("{name} is {age} years old", 
    std::format_arg("name", "Alice"),
    std::format_arg("age", 30));

// 对齐和填充
std::string s5 = std::format("{:<10}", "left");    // 左对齐
std::string s6 = std::format("{:>10}", "right");   // 右对齐
std::string s7 = std::format("{:^10}", "center");  // 居中
std::string s8 = std::format("{:*^10}", "center"); // 填充

// 数字格式
std::string s9 = std::format("{:b}", 42);   // 二进制
std::string s10 = std::format("{:o}", 42);  // 八进制
std::string s11 = std::format("{:x}", 42);  // 十六进制

// 自定义类型
struct Point {
    int x, y;
};

template<>
struct std::formatter<Point> {
    constexpr auto parse(auto& ctx) {
        return ctx.begin();
    }
    
    auto format(const Point& p, auto& ctx) const {
        return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
    }
};

Point p{1, 2};
std::string s12 = std::format("{}", p);

总结

版本演进

版本关键特性
C++11auto、Lambda、移动语义、智能指针
C++14泛型 Lambda、返回类型推导、make_unique
C++17结构化绑定、optional、variant、any
C++20概念、范围、协程、模块、format

选择建议

  1. 新项目:直接使用 C++20
  2. 旧项目升级:逐步引入新特性
  3. 跨平台项目:考虑编译器支持
  4. 性能关键:重点关注移动语义和智能指针

学习路径

  1. C++11:基础现代 C++
  2. C++14:增强和优化
  3. C++17:实用工具
  4. C++20:高级特性

学习资源


延伸阅读