std::atomic_ref为已存在变量提供临时原子访问能力,不创建新对象,需满足对齐和生命周期约束,仅支持trivially copyable且对齐足够的类型,非内存管理型视图。

c++20的std::atomic_ref有什么用? (对非原子对象进行原子操作)  第1张

std::atomic_ref 用来给普通变量临时加原子性

它不创建新对象,而是为已存在的 T 类型变量(如全局数组元素、结构体字段、栈上变量)提供原子访问能力。核心价值在于:避免为每个需要原子操作的变量都声明成 std::atomic,尤其当这些变量大部分时间并不需要原子性,只在少数同步点才需保护时。

必须满足对齐和生命周期约束,否则行为未定义

std::atomic_ref 构造时会检查底层对象是否满足原子操作所需的对齐要求(通常为 alignof(T)),且该对象在整个 std::atomic_ref 生命周期内不能被销毁或移动。常见翻车点:

  • 对栈上局部变量使用 std::atomic_ref 后,又在函数返回后继续访问 —— 悬垂引用
  • std::vector 元素取 std::atomic_ref,但 vector 发生了 reallocation —— 原地址失效
  • 对 packed 结构体成员(如 struct __attribute__((packed)) S { char a; int b; };)构造 std::atomic_ref —— 对齐不足,std::atomic_ref 构造可能抛 std::bad_optional_access 或直接 UB

支持的类型有限,不是所有 T 都能用

只有满足 std::is_trivially_copyable_v 且对齐足够(至少 alignof(T))的类型才能用于 std::atomic_ref。常见可用类型包括:intlong longvoid*std::shared_ptr;不可用类型包括:std::string、含虚函数的类、非 trivially copyable 的自定义结构体。

例如,下面这段代码是合法的:

立即学习“C++免费学习笔记(深入)”;

int x = 42;
std::atomic_ref ref{x};
ref.fetch_add(1, std::memory_order_relaxed); // x 变成 43

但若写成:

std::string s = "hello";
std::atomic_ref bad_ref{s}; // 编译失败:std::string 不满足 is_trivially_copyable

不能替代 std::atomic,也不提供默认初始化语义

std::atomic_ref 是“视图”,不是“容器”。它不管理内存,不参与对象生命周期,也不隐式保证初始值的原子可见性。比如:

  • 不能像 std::atomic a{0}; 那样带初值构造 —— 必须已有确定值的变量
  • 多个线程同时通过不同 std::atomic_ref 绑定到同一变量是允许的,但必须确保无数据竞争(即不能一个线程用 atomic_ref 写,另一个用普通读)
  • 如果原变量被其他非原子代码修改,std::atomic_ref 的操作仍能保证自身原子性,但无法防止与非原子访问之间的竞态 —— 这是使用者的责任

最容易被忽略的是:**对齐检查只在运行时构造发生,且某些平台(如 ARM)对未对齐原子操作直接触发硬件异常,而不是静默降级**。务必在绑定前确认目标变量地址满足 alignof(T),必要时用 alignas 显式对齐。