2

I have a class that has a member variable that wraps a std time_point in an atomic. I'm having a hard time getting older compilers happy with it. I initially had issues with versions of GCC earlier than 10 accepting it. I addressed this via explicitly initializing it.

I thought that made all the compilers happy. But once my code got into production, I faced an issue in the more thorough CI (in comparison to the PR CI) with older clang compilers. The project is Apache Traffic Server, so it is open sourced, if viewing it is interesting. Here is the code:

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

Here is a minimal example that demonstrates the problem:

#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{}};
};

The error can be reproduced in godbolt: https://godbolt.org/z/4db4osf66

Here's the error:

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

Playing with the godbolt configuration, anything newer than clang 9 does not raise this error. I can ifdef around it, I suppose, but I'm not sure what the workaround is if I do. The explicit initialization made gcc happy. What can I do to make clang 8 and earlier happy with this construction?


By the way, this question is related to the following: Program with "noexcept" constructor accepted by gcc, rejected by clang

While that question discusses whether clang or gcc is right about the error or lack of error, I'm not concerned about what is theoretically correct for the compiler to do. It seems like both clang and gcc agree in later versions that this is OK as formed. That's good, but not helpful for a project that needs to support older compilers. My question asks what I can do to make the older compilers happy.

firebush
  • 5,180
  • 4
  • 34
  • 45
  • 1
    `std::atomic _last_allowed_time(TimePoint());` [seems to work](https://godbolt.org/z/1sPcMd14M) (from Clang 5.0.0 onwards). – Paul Sanders Mar 29 '21 at 23:08
  • Does `A()=default` fix it? – Mooing Duck Mar 29 '21 at 23:27
  • @PaulSanders That works. Thank you. Nice and easy. If you post it as an answer I will accept it. – firebush Mar 30 '21 at 00:20
  • 1
    @PaulSanders: actually, it compiles, but I think it's doing something different than we expect. In the use of that "variable" it now says that it's interpreted as a "non-static member function". So I think that line is interpreted as a function rather than as a member variable. – firebush Mar 30 '21 at 00:25
  • @MooingDuck: no, that does not fix the issue. – firebush Mar 30 '21 at 01:20

1 Answers1

2

As a workaround, you can make TimePoint a subclass (with a noexcept default constructor) instead of a 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_;
};

This compiles (with -std=c++1y) on Godbolt for all GCC ≥4.8.1 and clang ≥3.4.1.

Edit: Also, just so you're aware, identifiers beginning with underscores are reserved for the implementation. I like to use trailing underscores (i.e. last_allowed_time_ not _last_allowed_time).

Ray Hamel
  • 1,289
  • 6
  • 16