为什么std::atomic中的所有成员函数都同时出现在有volatile和没有volatile的情况下

Why do all the member functions in std::atomic appear both with and without volatile?

本文关键字:volatile 情况下 函数 atomic std 成员 为什么      更新时间:2023-10-16

我注意到std::atomic<T>类型的大多数成员函数都声明了两次,一次使用volatile修饰符,一次不使用(示例(。我检查了G++标准库实现的源代码,发现它们都是完全重复的,例如

bool
load(memory_order __m = memory_order_seq_cst) const noexcept
{ return _M_base.load(__m); }
bool
load(memory_order __m = memory_order_seq_cst) const volatile noexcept
{ return _M_base.load(__m); }

我找不到任何例子表明volatile变体的行为与非volatile变体不同,返回类型或诸如此类的东西也不同

为什么?我认为volatile成员函数也可以在非volatile的对象中调用。因此,声明和定义std::atomic::load(...) const volatile noexcept等就足够了。

更新:

根据这些评论,我的问题基本上可以归结为:你能提供一个例子吗?在以下两种情况下,使用volatile实例(不一定是std::atomic(的一些调用会生成不同的程序集,

  1. 每个成员函数都出现在具有和不具有volatile、的同一主体中

  2. 只有volatile变体存在?

这是假设编译器可以进行标准允许的任何优化(或者只是最高优化级别(。

也许这一切都源于volatile是什么,请参阅这个答案。由于与通常的应用程序开发相比,用例非常少,所以通常没有人关心。我假设您没有任何实际的场景,您想知道是否应该应用这些不稳定的过载。然后我会尝试想出一个你可能需要的例子(不要认为它太真实(。

volatile std::sig_atomic_t status = ~SIGINT;
std::atomic<int> shareable(100);
void signal_handler(int signal)
{
status = signal;
}
// thread 1
auto old = std::signal(SIGINT, signal_handler);
std::raise(SIGINT);
int s = status;
shareable.store(10, std::memory_order_relaxed);
std::signal(SIGINT, old);
// thread 2
int i = shareable.load(std::memory_order_relaxed);

memory_order_relaxed保证原子性和修改顺序的一致性,没有副作用。volatile不能因副作用而重新排序。那么,在线程2中,您可以得到等于10的shareable,但状态仍然不是SIGINT。但是,如果将类型限定符设置为必须保证的shareablevolatile。为此,您需要成员方法是volatile限定的。

你为什么要做这样的事?我可能会想到的一种情况是,您有一些旧代码使用了旧的基于volatile的东西,并且由于某种原因无法修改它。很难想象,但我想atomicvolatile内联程序集之间可能需要某种有保证的顺序。IMHO的底线是,只要有可能,您就可以使用新的原子库而不是volatile对象,如果有一些volatile对象无法删除,并且您想使用atomic对象,那么您可能需要atomic对象的volatile限定符来具有适当的排序保证,因此您需要重载。

更新

但如果我只想让原子类型同时可用作易失性和非易失性,为什么不实现前者呢?

struct Foo {
int k;
};
template <typename T>
struct Atomic {
void store(T desired) volatile { t = desired; }
T t;
};
int main(int i, char** argv) {
//error: no viable overloaded '='
// void store(T desired) volatile { t = desired; }
Atomic<Foo>().store(Foo());
return 0;
}

load和其他操作也是如此,因为它们通常不是需要复制运算符和/或复制构造函数(也可以是volatile或非volatile(的琐碎实现。

相关文章: