首页 > 解决方案 > 如何让旧版本的 clang 对 atomic 的默认异常规范感到满意

问题描述

我有一个类,它有一个将 std time_point 包装在原子中的成员变量。我很难让较旧的编译器对此感到满意。我最初在接受 10 之前的 GCC 版本时遇到了问题。我通过显式初始化它来解决这个问题。

我认为这让所有编译器都很高兴。但是,一旦我的代码投入生产,我在使用较旧的 clang 编译器的更彻底的 CI(与 PR CI 相比)中遇到了一个问题。该项目是 Apache Traffic Server,所以它是开源的,如果查看它是有趣的。这是代码:

https://github.com/apache/trafficserver/blob/master/include/tscore/Throttler.h#L117

这是一个演示问题的最小示例:

#include <atomic>
#include <chrono>

struct A {
  A() {}

  using Clock = std::chrono::system_clock;
  using TimePoint = Clock::time_point;

  // The explicit initialization is needed by
  // GCC earlier than 10.
  std::atomic<TimePoint> _last_allowed_time{TimePoint{}};
};

该错误可以在godbolt中重现: https ://godbolt.org/z/4db4osf66

这是错误:

In file included from <source>:1:
/opt/compiler-explorer/gcc-8.3.0/lib/gcc/x86_64-linux-gnu/8.3.0/../../../../include/c++/8.3.0/atomic:194:7: error: exception specification of explicitly defaulted default constructor does not match the calculated one
      atomic() noexcept = default;
      ^
<source>:12:26: note: in instantiation of template class 'std::atomic<std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long, std::ratio<1, 1000000000> > > >' requested here
  std::atomic<TimePoint> _last_allowed_time{TimePoint{}};
                         ^
1 error generated.
Compiler returned: 1

使用 Godbolt 配置,任何比 clang 9 更新的东西都不会引发此错误。我想我可以 ifdef 解决它,但我不确定如果我这样做的解决方法是什么。显式初始化让 gcc 很高兴。我能做些什么来让 clang 8 和更早的版本对这个结构感到满意?


顺便说一句,这个问题与以下内容有关: gcc接受的带有“noexcept”构造函数的程序,被clang拒绝

虽然该问题讨论了 clang 或 gcc 对于错误或缺少错误是否正确,但我并不关心编译器在理论上做什么是正确的。在以后的版本中,clang 和 gcc 似乎都同意这在形成时是可以的。这很好,但对于需要支持旧编译器的项目没有帮助。我的问题是问我可以做些什么来让旧的编译器满意。

标签: c++gccclang

解决方案


作为一种解决方法,您可以创建TimePoint一个子类(使用noexcept默认构造函数)而不是 typedef。

#include <atomic>
#include <chrono>

struct A {
  A() {}

  using Clock = std::chrono::system_clock;
  class TimePoint : public Clock::time_point {
  public:
    using time_point::time_point;
    constexpr TimePoint() noexcept : time_point() {}
  };

  std::atomic<TimePoint> last_allowed_time_;
};

-std=c++1y对于所有 GCC ≥4.8.1 和 clang ≥3.4.1,这会在 Godbolt 上编译(使用)。

编辑:另外,请注意,以下划线开头的标识符保留用于实现。我喜欢使用尾随下划线(即last_allowed_time_not _last_allowed_time)。


推荐阅读