第26天:C++20新特性
学习目标
了解和掌握C++20标准引入的重要新特性,学会使用最新的现代C++特性来编写更安全、更高效、更易维护的代码。
学习资源链接
📚 官方文档和教程
🎥 视频教程
📖 深入阅读
🔧 编译器支持
学习内容
1. 概念 (Concepts)
概念是C++20引入的重要特性,用于约束模板参数,提供更好的错误信息和更清晰的接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| #include <concepts> #include <iostream> #include <vector> #include <type_traits>
template<typename T> concept Integral = std::is_integral_v<T>;
template<typename T> concept FloatingPoint = std::is_floating_point_v<T>;
template<typename T> concept Arithmetic = Integral<T> || FloatingPoint<T>;
template<Arithmetic T> T add(T a, T b) { return a + b; }
template<typename T> concept Printable = requires(T t) { std::cout << t; };
template<typename T> concept Container = requires(T t) { typename T::value_type; typename T::iterator; t.begin(); t.end(); t.size(); };
template<typename T> concept Sortable = Container<T> && requires(T t) { std::sort(t.begin(), t.end()); };
template<typename T> concept Drawable = requires(T obj, int x, int y) { obj.draw(); obj.move(x, y); { obj.getPosition() } -> std::convertible_to<std::pair<int, int>>; };
class Circle { public: void draw() const { std::cout << "Drawing circle at (" << x_ << ", " << y_ << ")\n"; } void move(int x, int y) { x_ = x; y_ = y; } std::pair<int, int> getPosition() const { return {x_, y_}; } private: int x_ = 0, y_ = 0; };
template<Drawable T> void render(const T& shape) { shape.draw(); }
template<typename T> concept Number = Integral<T> || FloatingPoint<T>;
template<Number T> constexpr T abs(T value) { if constexpr (std::is_signed_v<T>) { return value < 0 ? -value : value; } else { return value; } }
void concepts_examples() { std::cout << add(5, 3) << std::endl; std::cout << add(2.5, 1.5) << std::endl; std::vector<int> vec = {3, 1, 4, 1, 5}; static_assert(Container<decltype(vec)>); static_assert(Sortable<decltype(vec)>); Circle circle; render(circle); std::cout << "abs(-5) = " << abs(-5) << std::endl; std::cout << "abs(3.14) = " << abs(-3.14) << std::endl; }
|
2. 协程 (Coroutines)
协程提供了一种编写异步代码的新方式,使得异步编程更加直观和易于理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
| #include <coroutine> #include <iostream> #include <thread> #include <chrono> #include <optional>
struct Task { struct promise_type { 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_void() {} void unhandled_exception() {} }; std::coroutine_handle<promise_type> coro; Task(std::coroutine_handle<promise_type> h) : coro(h) {} ~Task() { if (coro) coro.destroy(); } Task(const Task&) = delete; Task& operator=(const Task&) = delete; Task(Task&& other) noexcept : coro(other.coro) { other.coro = {}; } Task& operator=(Task&& other) noexcept { if (this != &other) { if (coro) coro.destroy(); coro = other.coro; other.coro = {}; } return *this; } };
template<typename T> struct Generator { struct promise_type { T current_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 value) { current_value = value; return {}; } void return_void() {} void unhandled_exception() {} }; std::coroutine_handle<promise_type> coro; Generator(std::coroutine_handle<promise_type> h) : coro(h) {} ~Generator() { if (coro) coro.destroy(); } Generator(const Generator&) = delete; Generator& operator=(const Generator&) = delete; Generator(Generator&& other) noexcept : coro(other.coro) { other.coro = {}; } Generator& operator=(Generator&& other) noexcept { if (this != &other) { if (coro) coro.destroy(); coro = other.coro; other.coro = {}; } return *this; } bool next() { coro.resume(); return !coro.done(); } T value() { return coro.promise().current_value; } };
Task simple_coroutine() { std::cout << "Coroutine started\n"; co_await std::suspend_always{}; std::cout << "Coroutine resumed\n"; }
Generator<int> fibonacci() { int a = 0, b = 1; while (true) { co_yield a; auto temp = a; a = b; b = temp + b; } }
Generator<int> range(int start, int end) { for (int i = start; i < end; ++i) { co_yield i; } }
struct AsyncTask { struct promise_type { std::optional<int> result; AsyncTask get_return_object() { return AsyncTask{std::coroutine_handle<promise_type>::from_promise(*this)}; } std::suspend_never initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_value(int value) { result = value; } void unhandled_exception() {} }; std::coroutine_handle<promise_type> coro; AsyncTask(std::coroutine_handle<promise_type> h) : coro(h) {} ~AsyncTask() { if (coro) coro.destroy(); } bool is_ready() const { return coro.done(); } int get_result() { return coro.promise().result.value_or(0); } };
AsyncTask async_computation(int n) { std::cout << "Starting async computation for " << n << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); int result = n * n; std::cout << "Computation completed: " << result << std::endl; co_return result; }
void coroutines_examples() { auto task = simple_coroutine(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::cout << "\nFibonacci sequence:\n"; auto fib = fibonacci(); for (int i = 0; i < 10 && fib.next(); ++i) { std::cout << fib.value() << " "; } std::cout << std::endl; std::cout << "\nRange 5-10:\n"; auto r = range(5, 10); while (r.next()) { std::cout << r.value() << " "; } std::cout << std::endl; std::cout << "\nAsync computation:\n"; auto async_task = async_computation(7); while (!async_task.is_ready()) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::cout << "Final result: " << async_task.get_result() << std::endl; }
|
3. 模块 (Modules)
模块是C++20引入的新的代码组织方式,用于替代传统的头文件包含机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| export module math_utils;
import <iostream>; import <cmath>;
export namespace math_utils { double add(double a, double b) { return a + b; } double multiply(double a, double b) { return a * b; } double power(double base, double exp) { return std::pow(base, exp); } export class Calculator { private: double memory = 0.0; public: void store(double value) { memory = value; } double recall() const { return memory; } double calculate(double a, double b, char op) { switch (op) { case '+': return add(a, b); case '*': return multiply(a, b); case '^': return power(a, b); default: return 0.0; } } }; }
namespace { void internal_function() { std::cout << "This is internal to the module\n"; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| export module string_utils;
import <string>; import <vector>; import <algorithm>; import <sstream>;
export namespace string_utils { std::vector<std::string> split(const std::string& str, char delimiter) { std::vector<std::string> tokens; std::stringstream ss(str); std::string token; while (std::getline(ss, token, delimiter)) { tokens.push_back(token); } return tokens; } std::string join(const std::vector<std::string>& strings, const std::string& separator) { if (strings.empty()) return ""; std::string result = strings[0]; for (size_t i = 1; i < strings.size(); ++i) { result += separator + strings[i]; } return result; } std::string to_upper(const std::string& str) { std::string result = str; std::transform(result.begin(), result.end(), result.begin(), ::toupper); return result; } std::string to_lower(const std::string& str) { std::string result = str; std::transform(result.begin(), result.end(), result.begin(), ::tolower); return result; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import math_utils; import string_utils; import <iostream>; import <vector>;
int main() { std::cout << "Math operations:\n"; std::cout << "2 + 3 = " << math_utils::add(2, 3) << std::endl; std::cout << "4 * 5 = " << math_utils::multiply(4, 5) << std::endl; std::cout << "2^8 = " << math_utils::power(2, 8) << std::endl; math_utils::Calculator calc; calc.store(100); std::cout << "Calculator memory: " << calc.recall() << std::endl; std::cout << "\nString operations:\n"; std::string text = "hello,world,cpp20"; auto parts = string_utils::split(text, ','); std::cout << "Split result: "; for (const auto& part : parts) { std::cout << "[" << part << "] "; } std::cout << std::endl; std::string joined = string_utils::join(parts, " | "); std::cout << "Joined: " << joined << std::endl; std::cout << "Uppercase: " << string_utils::to_upper(joined) << std::endl; std::cout << "Lowercase: " << string_utils::to_lower(joined) << std::endl; return 0; }
|
4. 范围 (Ranges)
C++20引入了ranges库,提供了更强大和易用的算法和视图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| #include <ranges> #include <vector> #include <iostream> #include <algorithm> #include <string>
namespace ranges = std::ranges; namespace views = std::views;
void ranges_examples() { std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::cout << "Original: "; ranges::copy(numbers, std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; auto it = ranges::find(numbers, 5); if (it != numbers.end()) { std::cout << "Found 5 at position " << std::distance(numbers.begin(), it) << std::endl; } std::cout << "\nViews examples:\n"; auto even_numbers = numbers | views::filter([](int n) { return n % 2 == 0; }); std::cout << "Even numbers: "; for (int n : even_numbers) { std::cout << n << " "; } std::cout << std::endl; auto squared = numbers | views::transform([](int n) { return n * n; }); std::cout << "Squared: "; for (int n : squared) { std::cout << n << " "; } std::cout << std::endl; auto even_squares = numbers | views::filter([](int n) { return n % 2 == 0; }) | views::transform([](int n) { return n * n; }); std::cout << "Even squares: "; for (int n : even_squares) { std::cout << n << " "; } std::cout << std::endl; auto first_five = numbers | views::take(5); std::cout << "First 5: "; ranges::copy(first_five, std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; auto skip_three = numbers | views::drop(3); std::cout << "Skip first 3: "; ranges::copy(skip_three, std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; auto reversed = numbers | views::reverse; std::cout << "Reversed: "; ranges::copy(reversed, std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; std::string text = "Hello World C++20"; auto words = text | views::split(' '); std::cout << "\nWords in text:\n"; for (const auto& word : words) { std::cout << "- "; ranges::copy(word, std::ostream_iterator<char>(std::cout)); std::cout << std::endl; } auto iota_range = views::iota(1, 11); std::cout << "Iota 1-10: "; ranges::copy(iota_range, std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; std::vector<std::string> words_vec = {"apple", "banana", "cherry", "date", "elderberry"}; auto long_words_upper = words_vec | views::filter([](const std::string& s) { return s.length() > 5; }) | views::transform([](const std::string& s) { std::string upper = s; ranges::transform(upper, upper.begin(), ::toupper); return upper; }); std::cout << "Long words (uppercase): "; for (const auto& word : long_words_upper) { std::cout << word << " "; } std::cout << std::endl; }
|
5. 三路比较运算符 (Spaceship Operator <=>)
三路比较运算符简化了比较操作的实现,可以自动生成所有比较运算符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| #include <compare> #include <iostream> #include <string>
struct Point { int x, y; auto operator<=>(const Point&) const = default;
};
class Person { private: std::string name; int age; double salary; public: Person(std::string n, int a, double s) : name(std::move(n)), age(a), salary(s) {} std::strong_ordering operator<=>(const Person& other) const { if (auto cmp = age <=> other.age; cmp != 0) { return cmp; } if (auto cmp = name <=> other.name; cmp != 0) { return cmp; } return salary <=> other.salary; } bool operator==(const Person& other) const { return (*this <=> other) == 0; } void print() const { std::cout << name << " (age: " << age << ", salary: " << salary << ")"; } };
struct Temperature { double celsius; std::partial_ordering operator<=>(const Temperature& other) const { if (std::isnan(celsius) || std::isnan(other.celsius)) { return std::partial_ordering::unordered; } return celsius <=> other.celsius; } bool operator==(const Temperature& other) const { return (*this <=> other) == 0; } };
void spaceship_examples() { Point p1{1, 2}; Point p2{1, 3}; Point p3{1, 2}; std::cout << "Point comparisons:\n"; std::cout << "p1 == p3: " << (p1 == p3) << std::endl; std::cout << "p1 < p2: " << (p1 < p2) << std::endl; std::cout << "p1 > p2: " << (p1 > p2) << std::endl; Person alice("Alice", 30, 50000); Person bob("Bob", 25, 45000); Person charlie("Charlie", 30, 55000); std::cout << "\nPerson comparisons:\n"; std::cout << "Alice vs Bob: "; alice.print(); std::cout << " vs "; bob.print(); if (alice > bob) { std::cout << " -> Alice is greater\n"; } else { std::cout << " -> Bob is greater or equal\n"; } std::cout << "Alice vs Charlie: "; alice.print(); std::cout << " vs "; charlie.print(); if (alice < charlie) { std::cout << " -> Alice is less\n"; } else { std::cout << " -> Charlie is less or equal\n"; } Temperature t1{20.0}; Temperature t2{25.0}; Temperature t3{std::numeric_limits<double>::quiet_NaN()}; std::cout << "\nTemperature comparisons:\n"; std::cout << "20°C < 25°C: " << (t1 < t2) << std::endl; std::cout << "20°C == NaN: " << (t1 == t3) << std::endl; auto cmp = t1 <=> t3; if (cmp == std::partial_ordering::unordered) { std::cout << "20°C and NaN are unordered\n"; } }
|
其他C++20特性
指定初始化器 (Designated Initializers)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct Config { std::string name; int port = 8080; bool debug = false; };
void designated_initializers() { Config cfg{ .name = "MyServer", .port = 9000, .debug = true }; std::cout << "Config: " << cfg.name << ":" << cfg.port << " (debug: " << cfg.debug << ")\n"; }
|
consteval 函数
1 2 3 4 5 6 7 8 9 10 11 12
| consteval int factorial(int n) { return (n <= 1) ? 1 : n * factorial(n - 1); }
void consteval_example() { constexpr int fact5 = factorial(5); std::cout << "5! = " << fact5 << std::endl; }
|
实践练习
练习1:概念驱动设计
使用concepts设计一个通用的容器算法库:
- 定义不同类型的容器概念
- 实现基于概念的算法重载
- 提供清晰的错误信息
练习2:协程异步框架
实现一个简单的异步任务框架:
- 支持异步任务链
- 实现简单的任务调度器
- 支持错误处理
练习3:模块化数学库
使用modules创建一个数学计算库:
练习4:范围处理管道
使用ranges实现数据处理管道:
今日总结
通过学习C++20新特性,你应该掌握:
- 使用concepts约束模板参数,提供更好的错误信息
- 使用协程编写异步和生成器代码
- 使用modules组织大型项目代码
- 使用ranges简化算法和数据处理
- 使用三路比较运算符简化比较操作
明天预告
明天我们将进行项目实战,实现常用的数据结构,包括链表、栈、队列、二叉搜索树和哈希表等。
返回第四周 | 上一天:第25天 | 下一天:第27天