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

时间:2018-07-24 08:32:18

标签: c++ c++11 volatile

我注意到大多数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变体?

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

1 个答案:

答案 0 :(得分:1)

可能全部源于volatile,为此,请参见this answer。与常规应用程序开发相比,用例非常狭小,这就是为什么通常没有人关心的原因。我将假设您没有任何实际的场景,您想知道是否应该应用那些易失性重载。然后,我将尝试举一个示例,您可能需要这些示例(不要认为它过于真实)。

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中获得shareable等于10的状态,但是状态仍然不是SIGINT。但是,如果将类型限定符设置为volatile中的shareable,则必须保证。为此,您将需要volatile限定成员方法。

您为什么要这样做?我可能想到的一种情况是,您有一些使用基于volatile的旧代码的旧代码,并且由于一个或另一个原因而无法对其进行修改。难以想象,但是我想在atomicvolatile内联汇编之间可能需要某种保证顺序。底线,恕我直言,是只要有可能,您就可以使用新的原子库而不是volatile对象,如果有一些volatile对象是您无法摆脱的,并且您想使用atomic对象,那么您可能需要volatile限定符来使atomic对象具有适当的顺序保证,因为这将需要重载。

更新

  

但是,如果我只想让原子类型既可以使用易失性也可以使用非易失性,那么为什么不仅仅实现前者呢?

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的简单实现。 )。