C++学习(十)(标准,C++11 和 C++14,C++17,C++20)
C++ 标准
C++ 标准是一组定义语言功能、语法和语义的规则和准则。国际标准化组织 (ISO) 负责维护和更新 C++ 标准。这些标准的主要目的是确保跨多个平台和编译器的一致性、效率和可维护性。
以下是迄今为止发布的不同 C++ 标准的简要摘要:
-
C++98/C++03:C++ 的第一个标准化版本,引入了许多功能,如模板、异常和标准模板库 (STL)。C++03 是对 C++98 的次要更新,修复了一些错误并改进了性能。
-
C++11:对语言的重大升级,引入了以下功能:
- Lambda 表达式:
auto sum = [](int a, int b) -> int { return a + b; };
- 基于范围的 for 循环:
std::vector<int> numbers = {1, 2, 3, 4}; for (int num : numbers) { std::cout << num << std::endl; }
- 智能指针(如 和 )。
std::shared_ptr
std::unique_ptr
-
C++14:对 C++11 进行了一次次要更新,其中添加了如下功能:
- 通用 lambda 表达式:
auto generic_sum = [](auto a, auto b) { return a + b; };
- 二进制文本:
int binary_number = 0b1010;
-
C++17:另一个重大更新,引入了如下功能:
if
和初始值设定项:switch
if (auto it = my_map.find(key); it != my_map.end()) { // use 'it' here }
- 结构化绑定:
std::map<std::string, int> my_map = {{"A", 1}, {"B", 2}}; for (const auto& [key, value] : my_map) { // use 'key' and 'value' here }
-
C++20:该语言的最新重大更新,具有以下功能:
- 概念:
template<typename T> concept Addable = requires(T a, T b) { { a + b } -> std::same_as<T>; };
- 范围:
std::vector<int> numbers = {1, 2, 3, 4}; auto doubled = numbers | std::views::transform([](int n) { return n * 2; });
- 协程等。
请记住,要使用这些语言功能,您可能需要将编译器配置为使用特定的 C++ 标准版本。例如,对于 GCC 或 Clang,您可以使用 、 、 或 标志。-std=c++11
-std=c++14
-std=c++17
-std=c++20
C++11 和 C++14
C++11C++11 标准,也称为 C++0x,于 2011 年 9 月正式发布。它引入了几项新的语言功能和改进,包括:
-
Auto:允许编译器根据变量的初始化表达式推断变量类型。
auto integer = 42; // integer is of int type auto floating = 3.14; // floating is of double type
-
基于范围的 for 循环:提供类似于 foreach 的语义,用于迭代容器或数组。
std::vector<int> numbers {1, 2, 3, 4}; for (int number : numbers) { std::cout << number << std::endl; }
-
Lambda 函数:允许更轻松地创建函数对象的匿名函数。
auto add = [](int a, int b) -> int { return a + b; }; int sum = add(42, 13); // sum is equal to 55
-
nullptr:一个表示 null 指针的新关键字,比使用文本 '0' 或 “NULL” 更类型安全。
int *ptr = nullptr;
-
线程支持库:提供一种使用线程和跨线程同步数据访问的标准方法。
std::thread t([]() { std::cout << "Hello from another thread\n"; }); t.join();
C++14C++14 标准于 2014 年 12 月正式发布,作为 C++11 的小型扩展,更侧重于微调语言功能和修复问题。引入的一些新功能:
-
通用 Lambda:允许使用 'auto' 类型占位符声明 lambda 函数参数。
auto add = [](auto a, auto b) { return a + b; }; auto sum_i = add(42, 13); // Still works with integers auto sum_f = add(3.14, 2.72); // Now works with doubles too
-
Binary Literals(二进制文本):允许您将整数作为二进制文本输入,以提高可读性。
int b = 0b110101; // Decimal value is 53
-
decltype(auto):推导变量的类型以匹配初始化变量时使用的表达式的类型。
auto func = [](auto a, auto b) { return a * b; }; decltype(auto) result = func(5, 3.14); // decltype(auto) deduces to "double"
-
变量模板:允许您使用模板参数定义变量。
template <typename T> constexpr T pi = T(3.1415926535897932385); float r = pi<float>; // Instantiated as a float double d = pi<double>; // Instantiated as a double
C++20
C++20 是 C++ 编程语言的最新标准,它为语言带来了重大改进和新功能。此版本旨在促进更好的软件开发实践,并使开发人员能够编写更高效、可读和可维护的代码。
以下是 C++20 中引入的一些关键功能:
概念
概念是一种对模板参数强制实施特定要求的方法,允许您编写更具表现力和可理解性的代码。它们改进了使用模板时的错误消息,并确保模板参数满足特定条件。
template <typename T>
concept Addable = requires (T a, T b) {
{ a + b } -> std::same_as<T>;
};
template <Addable T>
T add(T a, T b) {
return a + b;
}
范围
范围提供了一种处理值序列的新方法,增强了标准库算法的功能和表现力。基于范围的算法使使用序列变得更加容易和方便。
#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> numbers = { 1, 2, 3, 4, 5 };
auto even_numbers = numbers | std::views::filter([](int n) { return n % 2 == 0; });
for (int n : even_numbers) {
std::cout << n << ' ';
}
}
协程
协程是一种编写异步和并发代码的新方法,具有更高的可读性。它们允许暂停和恢复函数,使您能够编写更高效、无阻塞的代码。
#include <coroutine>
#include <iostream>
#include <future>
std::future<int> async_value(int value) {
co_await std::chrono::seconds(1);
co_return value * 2;
}
int main() {
auto result = async_value(42);
std::cout << "Result: " << result.get() << std::endl;
}
我们可以用**“定时烹饪任务”**的比喻来理解这段代码的异步协程机制:
代码拆解
1. 创建定时烹饪任务(协程函数)
std::future<int> async_value(int value) {
co_await std::chrono::seconds(1); // 暂停1秒(定时器)
co_return value * 2; // 返回结果(完成烹饪)
}
-
co_await
:
像给厨房设定一个定时器,在等待的1秒钟里,厨师(主线程)可以去做其他事情(不阻塞)。 -
co_return
:
定时器响后,自动继续执行,返回加工后的结果(把原料value
翻倍)。
2. 启动任务并获取结果
int main() {
auto result = async_value(42); // 开始烹饪(启动协程)
std::cout << "Result: " << result.get() << std::endl; // 等待结果
}
-
result.get()
:
相当于等待厨师完成烹饪,如果结果还没准备好(定时未到),主线程会在这里暂停等待。
类比场景
想象你让智能厨房做菜:
-
下达指令:
async_value(42)
相当于说:“把42克食材处理一下,1小时后给我结果”。 -
异步等待:
厨房定时器开始倒计时,你可以去刷手机(主线程不阻塞)。 -
获取结果:
1小时后,你调用result.get()
取回加工好的84克成品。
关键知识点
代码部分 | 作用 | 类比 |
---|---|---|
std::future<int> | 表示未来将获得一个整数结果 | 烹饪订单凭据 |
co_await | 暂停协程,让出线程资源 | 设定厨房定时器 |
co_return | 协程完成时返回结果 | 厨师交付成品 |
result.get() | 阻塞等待结果 | 等待厨师完成 |
实际执行流程
-
调用
async_value(42)
启动协程:-
遇到
co_await
→ 暂停协程,启动1秒定时器。 -
立刻返回
future
对象(此时结果尚未准备好)。
-
-
主线程执行
result.get()
:-
如果1秒未到 → 主线程阻塞等待。
-
1秒后定时器触发 → 协程恢复,计算
42*2=84
并存入future
。 -
主线程获得结果,输出
Result: 84
。
-
注意事项
-
协程依赖:需要 C++20 或更高标准,且编译器支持协程(如 GCC11+/Clang14+)。
-
不完全是多线程:协程的暂停/恢复在单线程内也可实现(本例通过定时器演示异步,实际可能涉及线程池)。
-
错误处理:真实场景需处理协程中可能的异常(本例未展示)。
一句话总结
这段代码像**“智能厨房点单”**——主线程下单后继续干活,协程默默计时处理,最后交付结果,高效利用等待时间! 🕒👨🍳
和 关键字constexpr
consteval
和 都与编译时评估有关。标记为 的函数可以在编译时或运行时执行,而标记为 的函数只能在编译时执行。constexpr
consteval
constexpr
consteval
constexpr int add(int a, int b) {
return a + b;
}
consteval int square(int x) {
return x * x;
}
int main() {
constexpr int result1 = add(3, 4); // evaluated at compile-time
int result2 = add(5, 6); // evaluated at runtime
constexpr int result3 = square(7); // evaluated at compile-time
}
这些只是 C++20 标准的一些亮点。它还包括许多其他功能和改进,例如结构化绑定、改进的 lambda 和新的标准库组件。总体而言,C++20 使开发人员能够更轻松地编写干净、高效且富有表现力的代码。
我们可以用**“建筑设计”和“施工阶段”**的比喻来理解这两个关键字:
核心区别
关键字 | 作用阶段 | 比喻 | 灵活性 |
---|---|---|---|
constexpr | 编译时 或 运行时 | 设计蓝图(可调整) | 灵活,能适应不同阶段 |
consteval | 仅编译时 | 设计规范(必须确定) | 严格,仅设计阶段 |
详细解释
1. constexpr
(灵活的双面手)
-
功能:可以用于编译时计算,也可以在运行时当普通函数调用。
-
场景:
-
编译时:比如计算数组长度、模板参数等需要提前确定的值。
-
运行时:当输入参数只能在运行时确定时,自动降级为普通函数。
-
代码示例:
constexpr int 平方(int x) {
return x * x;
}
int main() {
constexpr int a = 平方(5); // 编译时计算(设计阶段确定)
int b = 平方(10); // 运行时计算(施工阶段执行)
}
2. consteval
(严格的编译时警察)
-
功能:强制函数必须在编译时执行,否则直接报错。
-
场景:
-
必须确保某个值在编译时确定(如模板元编程中的常量)。
-
避免运行时开销,确保优化。
-
代码示例:
consteval int 立方(int x) {
return x * x * x;
}
int main() {
constexpr int a = 立方(3); // 合法(编译时计算)
int b = 立方(5); // ❌ 编译错误(不能在运行时调用!)
}
对比场景
假设你是一个建筑师:
-
constexpr
像可调节的蓝图工具:-
可以在设计阶段(编译时)计算房间面积。
-
也可以在施工时(运行时)根据现场测量临时计算。
-
-
consteval
像严格的设计规范检查器:-
必须在设计阶段确定所有参数(如承重墙位置)。
-
施工阶段不允许临时修改,否则直接停工!
-
为什么需要 consteval
?
-
安全强制:确保某些关键计算(如加密密钥生成)不会泄露到运行时。
-
优化保证:编译时计算的结果可直接内联,提升性能。
-
错误前置:在编译阶段发现问题,避免运行时崩溃。
一句话总结
-
constexpr
:灵活多面手,编译时和运行时都能干活。 -
consteval
:严格检查员,只允许编译时干活! 🔧⚡