gpt4 book ai didi

c++ - 为什么 std::atomic 中的所有成员函数都带有和不带有 volatile?

转载 作者:可可西里 更新时间:2023-11-01 17:59:05 28 4
gpt4 key购买 nike

我注意到 std::atomic<T> 的大多数成员函数类型被声明了两次,一次是用 volatile修饰符,一次没有(example))。我检查了 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 是什么,参见 this answer .由于与通常的应用程序开发相比,用例非常少,这就是为什么通常没人关心的原因。我假设您没有任何实际场景想知道是否应该应用那些 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 内联汇编之间有某种保证顺序。最重要的是,恕我直言,只要有可能,您就可以使用新的原子库而不是 volatile 对象,如果有一些您无法删除的 volatile 对象的,并且您想使用 atomic 对象,那么您可能需要 volatile 限定符来为 atomic 对象提供正确的顺序保证,因为您需要重载。

更新

But if all I wanted was to have atomic types usable as both volatile and non-volatile, why not just implement the former?

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).

关于c++ - 为什么 std::atomic 中的所有成员函数都带有和不带有 volatile?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51494031/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com