Skip to content

Latest commit

 

History

History
790 lines (634 loc) · 19.2 KB

File metadata and controls

790 lines (634 loc) · 19.2 KB

C++20 深度语法手册

设计哲学:语言的根本性变革

C++20是C++历史上仅次于C++11的重大版本,其设计理念标志着语言的根本性转变:

  1. 约束编程:通过Concepts将类型要求显式化
  2. 异步原生化:Coroutines将异步编程提升到语言层面
  3. 模块化架构:Modules彻底改革代码组织方式
  4. 函数式增强:Ranges引入惰性求值和组合式编程
  5. 编译期强化:进一步扩展编译期计算能力

1. Concepts:类型约束的范式革命

SFINAE的终结者

// C++17的SFINAE噩梦
template<typename T>
std::enable_if_t<
    std::is_arithmetic_v<T> && 
    !std::is_same_v<T, bool> &&
    std::is_copy_constructible_v<T>,
    T
> 
complex_math_operation(T value) {
    return value * value + 1;
}

// C++20的优雅表达
template<std::integral T>
    requires (!std::same_as<T, bool>)
T complex_math_operation(T value) {
    return value * value + 1;
}

概念的定义机制

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

// 复合概念
template<typename T>
concept Container = requires(T container) {
    typename T::value_type;           // 嵌套类型要求
    typename T::iterator;
    container.begin();                // 表达式要求
    container.end();
    { container.size() } -> std::convertible_to<size_t>;  // 类型要求
    container.empty();                // 简单表达式要求
};

// 高级概念组合
template<typename T>
concept SortableContainer = Container<T> && requires(T container) {
    std::sort(container.begin(), container.end());
};

requires表达式的四种形式

template<typename T>
concept ComplexConcept = requires(T obj) {
    // 1. 简单要求:表达式必须有效
    obj.method();
    
    // 2. 类型要求:类型必须存在
    typename T::value_type;
    typename T::iterator;
    
    // 3. 复合要求:表达式有效且满足类型约束
    { obj.size() } -> std::convertible_to<size_t>;
    { obj.at(0) } -> std::same_as<typename T::value_type&>;
    
    // 4. 嵌套要求:满足其他概念
    requires std::default_initializable<T>;
    requires Numeric<typename T::value_type>;
};

概念的特化与重载

// 概念特化的函数重载
template<typename T>
void process(T value) {
    std::cout << "Generic processing\n";
}

template<std::integral T>
void process(T value) {
    std::cout << "Integer processing: " << value * 2 << '\n';
}

template<std::floating_point T>
void process(T value) {
    std::cout << "Float processing: " << value / 2.0 << '\n';
}

// 概念的部分特化
template<typename T>
class Storage {
    std::vector<T> data;
};

template<std::integral T>
class Storage<T> {
    std::vector<T> data;
    size_t bit_count = sizeof(T) * 8;  // 整数特化的额外功能
};

约束的短路求值

template<typename T>
concept SafeNumeric = requires {
    // 短路求值:如果T不是算术类型,后续检查不会进行
    requires std::is_arithmetic_v<T>;
    requires !std::is_same_v<T, bool>;
    requires std::is_copy_constructible_v<T>;
};

2. Ranges:函数式编程的胜利

视图(View)的惰性求值

#include <ranges>

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

// 传统STL的多步操作
std::vector<int> temp;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(temp),
             [](int n) { return n % 2 == 0; });

std::vector<int> result;
std::transform(temp.begin(), temp.end(), std::back_inserter(result),
               [](int n) { return n * n; });

// C++20 Ranges的组合式操作(惰性求值)
auto result_range = numbers 
    | std::views::filter([](int n) { return n % 2 == 0; })
    | std::views::transform([](int n) { return n * n; })
    | std::views::take(3);  // 只取前3个元素

// 此时还没有实际计算,只有在遍历时才计算
for (int value : result_range) {
    std::cout << value << " ";  // 现在才开始计算
}

自定义视图的实现

// 自定义一个斐波那契数列视图
class fibonacci_view : public std::ranges::view_interface<fibonacci_view> {
    size_t count_;
    
public:
    explicit fibonacci_view(size_t count) : count_(count) {}
    
    class iterator {
        size_t pos_, a_, b_;
    public:
        using value_type = size_t;
        using difference_type = std::ptrdiff_t;
        
        iterator(size_t pos = 0) : pos_(pos), a_(0), b_(1) {}
        
        size_t operator*() const { return a_; }
        
        iterator& operator++() {
            auto temp = a_ + b_;
            a_ = b_;
            b_ = temp;
            ++pos_;
            return *this;
        }
        
        bool operator==(const iterator& other) const {
            return pos_ == other.pos_;
        }
    };
    
    iterator begin() const { return iterator(0); }
    iterator end() const { return iterator(count_); }
};

// 使用自定义视图
auto fib_range = fibonacci_view(10) 
    | std::views::filter([](size_t n) { return n % 2 == 0; });

范围算法的性能优化

// 传统算法需要多次遍历
std::vector<std::string> words{"hello", "world", "cpp", "twenty"};
auto count = std::count_if(words.begin(), words.end(),
                          [](const auto& s) { return s.length() > 3; });

// Ranges算法的优化版本
auto count_optimized = std::ranges::count_if(words,
    [](const auto& s) { return s.length() > 3; });

// 投影(Projection)的强大功能
struct Person {
    std::string name;
    int age;
};

std::vector<Person> people{{"Alice", 30}, {"Bob", 25}, {"Carol", 35}};

// 直接基于成员排序,无需lambda
std::ranges::sort(people, {}, &Person::age);  // 按年龄排序

// 组合投影
auto adult_names = people
    | std::views::filter([](const Person& p) { return p.age >= 18; })
    | std::views::transform(&Person::name);

constexpr ranges

constexpr auto compute_at_compile_time() {
    std::array data{1, 2, 3, 4, 5};
    
    // 编译期范围操作
    auto result = data 
        | std::views::transform([](int x) { return x * x; })
        | std::views::filter([](int x) { return x > 10; });
    
    // 编译期聚合
    int sum = 0;
    for (int value : result) {
        sum += value;
    }
    return sum;
}

constexpr int compile_time_result = compute_at_compile_time();

3. Coroutines:异步编程的语言级支持

协程的三个关键字

#include <coroutine>

// generator协程
std::generator<int> fibonacci() {
    int a = 0, b = 1;
    while (true) {
        co_yield a;  // 暂停并返回值
        auto temp = a;
        a = b;
        b = temp + b;
    }
}

// task协程
std::task<int> async_computation(int x) {
    // 模拟异步操作
    co_await std::this_thread::sleep_for(std::chrono::milliseconds(100));
    co_return x * x;  // 返回值并结束协程
}

// 使用协程
auto fib_gen = fibonacci();
for (int i = 0; i < 10; ++i) {
    std::cout << fib_gen() << " ";  // 获取下一个值
}

协程的底层机制

// 协程的promise_type定义了协程行为
template<typename T>
struct task {
    struct promise_type {
        T value_;
        std::exception_ptr exception_;
        
        task get_return_object() {
            return task{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        
        std::suspend_never initial_suspend() { return {}; }  // 立即开始
        std::suspend_always final_suspend() noexcept { return {}; }  // 暂停在结尾
        
        void return_value(T value) { value_ = std::move(value); }
        void unhandled_exception() { exception_ = std::current_exception(); }
    };
    
    std::coroutine_handle<promise_type> coro_;
    
    explicit task(std::coroutine_handle<promise_type> coro) : coro_(coro) {}
    
    ~task() { 
        if (coro_) coro_.destroy(); 
    }
    
    T get_result() {
        if (!coro_.done()) {
            coro_.resume();  // 恢复协程执行
        }
        if (coro_.promise().exception_) {
            std::rethrow_exception(coro_.promise().exception_);
        }
        return std::move(coro_.promise().value_);
    }
};

自定义awaitable

struct timer_awaitable {
    std::chrono::milliseconds duration_;
    
    explicit timer_awaitable(std::chrono::milliseconds duration) 
        : duration_(duration) {}
    
    bool await_ready() const { return false; }  // 总是需要等待
    
    void await_suspend(std::coroutine_handle<> coro) {
        // 在其他线程中设置定时器
        std::thread([coro, this]() {
            std::this_thread::sleep_for(duration_);
            coro.resume();  // 时间到后恢复协程
        }).detach();
    }
    
    void await_resume() {}  // 恢复时无返回值
};

// 便利函数
timer_awaitable delay(std::chrono::milliseconds ms) {
    return timer_awaitable{ms};
}

// 使用自定义awaitable
std::task<void> timed_operation() {
    std::cout << "Starting operation...\n";
    co_await delay(std::chrono::milliseconds(1000));
    std::cout << "Operation completed after delay\n";
}

协程与异步I/O

// 异步文件读取协程
std::task<std::string> async_read_file(const std::string& filename) {
    // 异步打开文件
    auto file_handle = co_await async_open(filename);
    
    // 异步读取内容  
    auto content = co_await async_read_all(file_handle);
    
    // 异步关闭文件
    co_await async_close(file_handle);
    
    co_return content;
}

// 协程组合
std::task<void> process_files(const std::vector<std::string>& filenames) {
    std::vector<std::task<std::string>> tasks;
    
    // 启动所有异步读取任务
    for (const auto& filename : filenames) {
        tasks.push_back(async_read_file(filename));
    }
    
    // 等待所有任务完成
    for (auto& task : tasks) {
        auto content = co_await task;
        process_content(content);
    }
}

4. Modules:编译系统的革命

头文件地狱的终结

// 传统头文件的问题
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

#include <vector>
#include <algorithm>
#include <numeric>

class MathUtils {
public:
    static double average(const std::vector<double>& values);
    static double standard_deviation(const std::vector<double>& values);
};

#endif

// C++20模块的解决方案
// math_utils.cppm (模块接口文件)
export module math_utils;

import <vector>;
import <algorithm>;
import <numeric>;

export class MathUtils {
public:
    static double average(const std::vector<double>& values);
    static double standard_deviation(const std::vector<double>& values);
};

模块的内部实现

// math_utils.cpp (模块实现文件)
module math_utils;

import <cmath>;

double MathUtils::average(const std::vector<double>& values) {
    if (values.empty()) return 0.0;
    return std::accumulate(values.begin(), values.end(), 0.0) / values.size();
}

double MathUtils::standard_deviation(const std::vector<double>& values) {
    double avg = average(values);
    double variance = 0.0;
    
    for (double value : values) {
        variance += (value - avg) * (value - avg);
    }
    
    return std::sqrt(variance / values.size());
}

选择性导出

// advanced_math.cppm
export module advanced_math;

// 内部辅助函数,不导出
namespace internal {
    double helper_function(double x) {
        return x * x + 1;
    }
}

// 只导出公共接口
export namespace advanced_math {
    double complex_calculation(double x, double y) {
        return internal::helper_function(x) + internal::helper_function(y);
    }
    
    // 导出特定函数
    export double public_utility(double value);
}

// 不导出的私有实现
double private_calculation(double x) {
    return x * 2;  // 模块外部无法访问
}

模块分区

// graphics.cppm (主模块)
export module graphics;

export import :shapes;    // 导入并重新导出shapes分区
export import :colors;    // 导入并重新导出colors分区
import :internal_utils;   // 仅导入,不重新导出

// graphics-shapes.cppm (shapes分区)
export module graphics:shapes;

export class Circle {
    double radius_;
public:
    explicit Circle(double r) : radius_(r) {}
    double area() const;
};

// graphics-colors.cppm (colors分区)  
export module graphics:colors;

export enum class Color { Red, Green, Blue };
export Color mix_colors(Color a, Color b);

// graphics-internal.cppm (内部分区)
module graphics:internal_utils;

// 内部实用函数,不导出到模块外部
double pi() { return 3.14159265359; }

5. consteval:立即函数

强制编译期执行

// constexpr可以在运行时执行
constexpr int factorial_constexpr(int n) {
    return n <= 1 ? 1 : n * factorial_constexpr(n - 1);
}

// consteval强制在编译期执行
consteval int factorial_consteval(int n) {
    return n <= 1 ? 1 : n * factorial_consteval(n - 1);
}

void example() {
    constexpr int compile_time = 5;
    int runtime = 5;
    
    auto a = factorial_constexpr(compile_time);  // 编译期执行
    auto b = factorial_constexpr(runtime);       // 运行时执行
    
    auto c = factorial_consteval(compile_time);  // 编译期执行
    // auto d = factorial_consteval(runtime);    // 编译错误!
}

编译期配置和验证

consteval bool is_power_of_two(size_t value) {
    return value > 0 && (value & (value - 1)) == 0;
}

template<size_t BufferSize>
class FixedBuffer {
    static_assert(is_power_of_two(BufferSize), 
                  "Buffer size must be power of two");
    
    std::array<char, BufferSize> buffer_;
public:
    // ...
};

// 编译期字符串处理
consteval size_t string_length(const char* str) {
    const char* end = str;
    while (*end) ++end;
    return end - str;
}

template<size_t N>
consteval auto make_string_literal(const char (&str)[N]) {
    return str;
}

6. constinit:静态初始化保证

解决静态初始化顺序问题

// 传统方式可能存在初始化顺序问题
int expensive_computation() {
    // 复杂计算
    return 42;
}

// 可能在main之前或之后初始化
static int global_value = expensive_computation();

// C++20保证编译期初始化
constinit static int safe_global = 42;  // 保证在编译期初始化

// 也可以与consteval结合
consteval int compute_config() {
    return 100;
}

constinit static int config_value = compute_config();

7. 三向比较运算符 (<=>)

默认比较的自动生成

#include <compare>

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

// 现在可以使用所有比较运算符
Point p1{1, 2};
Point p2{3, 4};

bool equal = (p1 == p2);      // false
bool not_equal = (p1 != p2);  // true  
bool less = (p1 < p2);        // true
auto ordering = (p1 <=> p2);  // std::strong_ordering::less

自定义比较逻辑

struct Person {
    std::string name;
    int age;
    
    // 自定义比较:先按年龄,再按姓名
    std::strong_ordering operator<=>(const Person& other) const {
        if (auto cmp = age <=> other.age; cmp != 0) {
            return cmp;
        }
        return name <=> other.name;
    }
    
    bool operator==(const Person& other) const {
        return name == other.name && age == other.age;
    }
};

// 三种比较类别
class WeakComparable {
public:
    std::weak_ordering operator<=>(const WeakComparable& other) const {
        // 弱排序:等价但不相等的情况
        return std::weak_ordering::equivalent;
    }
};

class PartialComparable {
public:
    std::partial_ordering operator<=>(const PartialComparable& other) const {
        // 偏序:可能无法比较(如NaN)
        return std::partial_ordering::unordered;
    }
};

8. Lambda表达式的增强

模板lambda

// C++20支持模板参数
auto generic_lambda = []<typename T>(T value) {
    if constexpr (std::is_arithmetic_v<T>) {
        return value * 2;
    } else {
        return value;
    }
};

auto result1 = generic_lambda(42);      // int
auto result2 = generic_lambda(3.14);    // double
auto result3 = generic_lambda("hello"); // const char*

pack展开捕获

template<typename... Args>
auto make_lambda(Args... args) {
    // 捕获参数包
    return [args...](){ 
        return (args + ...);  // 折叠表达式
    };
}

auto sum_lambda = make_lambda(1, 2, 3, 4, 5);
int result = sum_lambda();  // 15

consteval lambda

auto compile_time_lambda = []() consteval {
    return 42;
};

constexpr int value = compile_time_lambda();  // 编译期执行

9. 其他重要特性

Designated Initializers

struct Config {
    std::string host = "localhost";
    int port = 8080;
    bool ssl_enabled = false;
    int timeout_ms = 5000;
};

// 指定初始化器,提高可读性
Config config{
    .host = "example.com",
    .port = 443,
    .ssl_enabled = true
    // timeout_ms使用默认值
};

std::format

#include <format>

std::string name = "Alice";
int age = 30;
double salary = 75000.50;

// 类型安全的格式化
std::string message = std::format(
    "Employee: {}, Age: {}, Salary: {:.2f}", 
    name, age, salary
);

// 命名参数
std::string formatted = std::format(
    "Hello {name}, you are {age} years old",
    std::make_format_args(name, age)
);

std::span

#include <span>

void process_data(std::span<const int> data) {
    // 统一处理数组、vector、array等
    for (int value : data) {
        std::cout << value << " ";
    }
}

int arr[] = {1, 2, 3, 4, 5};
std::vector<int> vec{6, 7, 8, 9, 10};
std::array<int, 3> arr_obj{11, 12, 13};

process_data(arr);      // C数组
process_data(vec);      // vector
process_data(arr_obj);  // array
process_data(std::span<int>{arr, 3});  // 子范围

std::jthread

#include <thread>
#include <stop_token>

void background_task(std::stop_token stop_token) {
    while (!stop_token.stop_requested()) {
        // 执行工作
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void example() {
    std::jthread worker(background_task);
    
    // 自动join,支持协作式取消
    std::this_thread::sleep_for(std::chrono::seconds(1));
    worker.request_stop();  // 请求停止
    
    // worker析构时自动join
}

设计哲学总结

C++20体现了C++语言设计的四个重要转向:

1. 从隐式到显式

  • Concepts让模板约束显式化
  • Modules让依赖关系显式化
  • consteval让编译期执行强制化

2. 从命令式到函数式

  • Ranges引入惰性求值和组合式编程
  • 协程支持声明式异步编程
  • 折叠表达式简化函数式操作

3. 从运行时到编译期

  • consteval/constinit扩展编译期计算
  • Concepts在编译期进行类型检查
  • 模板lambda增强编译期代码生成

4. 从复杂到简洁

  • 三向比较简化比较运算符定义
  • Designated initializers提高初始化的可读性
  • std::format提供类型安全的格式化

C++20不仅仅是语言特性的增加,而是编程范式的演进,为C++在现代软件开发中保持竞争力奠定了坚实基础。这些特性的组合使用将彻底改变C++代码的编写方式,让C++在保持性能优势的同时,获得了现代编程语言的表达力和安全性。