语言特性
指定初始化
- 按名称初始化结构体成员,提高可读性
1 2 3 4 5 6 7 8 9 10 11 |
struct Config { int width; int height; std::string title; }; Config cfg { .width = 800, .height = 600, .title = "App" }; |
[[likely]]
和[[unlikely]]
- 提示编译器分支预测优化
1 2 3 4 5 |
if (condition) [[likely]] { // 大概率执行的代码 } else [[unlikely]] { // 小概率执行的代码 } |
标准库新增组件
std::atomic_ref
(原子引用)
概述
C++20
引入的模板类,允许对 非原子类型 的对象进行原子操作- 它的核心作用是为现有的非原子变量提供原子访问能力,无需修改原始变量的类型或声明方式
- 适用于需要临时或局部原子操作的场景
特性
- 非侵入式原子操作
- 对普通变量(非
std::atomic
)进行原子操作,例如int
、float
或自定义类型
- 对普通变量(非
- 轻量级封装
- 不拥有目标对象,仅提供对其的原子访问接口
- 内存顺序控制
- 支持标准内存序(如
memory_order_relaxed
、memory_order_seq_cst
)
- 支持标准内存序(如
- 对齐要求
- 目标对象必须满足
std::atomic_ref<T>
的对齐要求(通常与std::atomic<T>
一致)
- 目标对象必须满足
场景
- 旧代码改造
- 无法修改原有变量的类型(例如第三方库中的全局变量),但需在多线程环境中安全访问
- 结构体成员原子化
- 对结构体中某个字段进行原子操作,无需将整个结构体声明为原子类型
- 临时原子操作
- 在特定代码段内需要原子性,但其他部分不需要
基本用法
- 原子化普通变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <atomic> #include <thread> #include <vector> int shared_data = 0; void increment() { std::atomic_ref<int> atomic_data(shared_data); atomic_data.fetch_add(1, std::memory_order_relaxed); } int main() { std::vector<std::jthread> threads; for (int i = 0; i < 10; ++i) { threads.emplace_back(increment); } } // shared_data 最终为 10 |
- 原子化结构体成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
struct Config { int value; bool enabled; }; Config config{0, false}; void update_config() { std::atomic_ref<int> atomic_val(config.value); atomic_val.store(42, std::memory_order_release); std::atomic_ref<bool> atomic_flag(config.enabled); atomic_flag.store(true, std::memory_order_release); } |
- 自定义类型的原子操作
- 若
T
是自定义类型,需满足std::atomic<T>
的要求(可平凡复制,支持按位原子操作):
- 若
1 2 3 4 5 6 7 8 9 10 |
struct Point { int x, y; }; Point pt{0, 0}; void move_point() { std::atomic_ref<Point> atomic_pt(pt); Point old = atomic_pt.load(std::memory_order_acquire); Point new_val{old.x + 1, old.y + 1}; atomic_pt.store(new_val, std::memory_order_release); } |
- 内存序控制
1 2 3 4 5 6 |
std::atomic_ref<int> ref(shared_data); int expected = 0; // 仅当 shared_data == 0 时,将其置为 42 ref.compare_exchange_strong(expected, 42, std::memory_order_acq_rel, std::memory_order_acquire); |
其他重要改进
std::source_location
(源码位置追踪)
概述
C++20
引入的标准库工具,用于在代码中捕获调用点的源码位置信息(如文件名、行号、函数名等)- 它取代了传统的
__FILE__
和__LINE__
宏,提供更安全、更灵活的方式记录代码上下文
- 它取代了传统的
作用
- 自动捕获调用点信息:
- 无需手动传递参数,自动记录日志、断言或调试信息的来源位置
- 类型安全:
- 通过结构体封装,避免宏的文本替换风险
- 支持编译期信息:
- 可在
constexpr
上下文中使用
- 可在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <source_location> #include <iostream> // 定义一个日志函数,自动捕获调用位置 void log(const std::string& message, const std::source_location& loc = std::source_location::current()) { std::cout << "File: " << loc.file_name() // 文件名 << "\nLine: " << loc.line() // 行号 << "\nFunction: " << loc.function_name() // 函数名 << "\nMessage: " << message << "\n\n"; } int main() { log("Hello from main!"); // 自动捕获调用位置 } |
示例
- 自动记录错误或日志的来源位置:
1 2 3 4 5 |
void assert_true(bool condition, const std::source_location& loc = ...) { if (!condition) { std::cerr << "Assert failed at " << loc.file_name() << ":" << loc.line(); } } |
- 标记代码段的执行路径:
1 2 3 |
void process_data(const std::source_location& loc = ...) { std::cout << "Data processed in " << loc.function_name() << "\n"; } |
- 结合
constexpr
生成编译期信息:
1 2 3 4 |
constexpr auto get_code_info() { return std::source_location::current(); } static_assert(get_code_info().line() > 0); // 编译期检查 |
std::remove_cvref
概述
C++20
引入的类型萃取工具,用于同时去除类型的 引用修饰符(&
或&&
)和CV
限定符(const
或volatile
)- 等价于先应用
std::remove_reference
再应用std::remove_cv
,简化了类型处理的步骤
- 等价于先应用
1 2 3 4 5 |
std::remove_cv_t<std::remove_reference_t<T>> = std::remove_cvref_t<T> |
对比
类型萃取工具 | 功能 | 示例输入 | 输出 |
std::remove_reference_t |
仅移除引用(& /&& ) |
const int& |
const int |
std::remove_cv_t |
仅移除 CV 限定符(const /volatile ) |
const volatile int |
int |
std::remove_cvref_t |
同时移除引用和 CV 限定符 | const volatile int& |
int |
使用场景
- 模板元编程中简化类型处理
1 2 3 4 5 6 7 |
#include <type_traits> template<typename T> void process(T&& param) { using RawType = std::remove_cvref_t<T>; // 去除引用和 CV // 例如:T = const int& → RawType = int } |
- 避免重复嵌套类型萃取
1 2 3 4 5 |
// C++17 需要两步操作 using Type1 = std::remove_cv_t<std::remove_reference_t<const int&>>; // → int // C++20 一步到位 using Type2 = std::remove_cvref_t<const int&>; // → int |
示例
- 基本用法
1 2 3 4 5 6 7 8 9 10 |
#include <type_traits> #include <iostream> int main() { using T1 = const volatile int&; // T1 = const volatile int& using T2 = std::remove_cvref_t<T1>; // T2 = int static_assert(std::is_same_v<T2, int>); std::cout << "T2 是 int 类型\n"; // 输出验证 } |
- 处理模板参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
template<typename T> void print_base_type() { using Base = std::remove_cvref_t<T>; if constexpr (std::is_same_v<Base, int>) { std::cout << "基础类型是 int\n"; } else if constexpr (std::is_same_v<Base, double>) { std::cout << "基础类型是 double\n"; } } int main() { print_base_type<const double&&>(); // 输出 "基础类型是 double" print_base_type<volatile int&>(); // 输出 "基础类型是 int" } |
std::type_identity
概述
C++20
引入的类型萃取工具,其核心作用是 保持类型不变,同时可用于控制模板参数推导或创建非推导上下文
功能
- 类型保持
std::type_identity<T>::type
始终为T
,不修改任何类型修饰符(如const
、引用等)
- 非推导上下文
- 将类型
T
包裹在std::type_identity
中,可阻止编译器自动推导模板参数,强制用户显式指定类型
- 将类型
场景
- 强制显式指定模板参数
1 2 3 4 5 6 7 8 9 10 |
template<typename T> void process(typename std::type_identity<T>::type value) { // T 必须显式指定,无法通过 value 推导 } int main() { // process(42); // 错误!无法推导 T process<int>(42); // 正确,显式指定 T=int process<double>(3.14); // 正确,显式指定 T=double } |
- 解决函数模板重载歧义
- 当多个重载函数模板可能导致推导冲突时,
std::type_identity
可强制类型匹配:
- 当多个重载函数模板可能导致推导冲突时,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
template<typename T> void log(typename std::type_identity<T>::type value) { std::cout << "Value: " << value << '\n'; } template<typename T> void log(typename std::type_identity<T>::type value, int precision) { std::cout << "Value: " << std::setprecision(precision) << value << '\n'; } int main() { log<int>(42); // 调用第一个重载 log<double>(3.1415, 2); // 调用第二个重载 } |
- 结合
SFINAE
控制模板实例化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
template<typename T> auto get_value_impl(int) -> std::type_identity_t<decltype(T::value)> { return T::value; } template<typename T> auto get_value_impl(...) -> std::type_identity_t<int> { return 0; } template<typename T> auto get_value() -> decltype(get_value_impl<T>(0)) { return get_value_impl<T>(0); } struct A { static int value; }; struct B {}; int main() { auto a = get_value<A>(); // 返回 A::value(int 类型) auto b = get_value<B>(); // 返回 0(int 类型) } |
示例
- 定义
1 2 3 4 5 6 7 8 9 10 |
#include <type_traits> template<typename T> struct type_identity { using type = T; // 原样保留类型 T }; // 辅助类型别名 template<typename T> using type_identity_t = typename type_identity<T>::type; |
- 示例
1 2 |
using T1 = std::type_identity<int>::type; // T1 = int using T2 = std::type_identity_t<const int&>; // T2 = const int& |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ C++_函数模板、类模板、特化、模板元编程、SFINAE、概念06/22
- ♥ Soui三05/19
- ♥ Soui七06/02
- ♥ STL_内存处理工具05/02
- ♥ C++11_四种类型转换11/10
- ♥ 51CTO:Linux C++网络编程四08/19