Should class be not trivially destructible, if standard specifies that it has a destructor?

Consider std::latch [thread.latch.class]:

namespace std {
  class latch {
  public:
    static constexpr ptrdiff_t max() noexcept;

    constexpr explicit latch(ptrdiff_t expected);
    ~latch();

    latch(const latch&) = delete;
    latch& operator=(const latch&) = delete;

    void count_down(ptrdiff_t update = 1);
    bool try_wait() const noexcept;
    void wait() const;
    void arrive_and_wait(ptrdiff_t update = 1);

  private:
    ptrdiff_t counter;  // exposition only
  };
}

Note that destructor is present. This means that this should hold:

static_assert(std::is_trivially_destructible_v<std::latch> == false);

However, it is opposite for the implementations (MSVC STL, libstdc++, libc++): https://godbolt.org/z/6s8173zTc

Is this:

  • Implementation freedom (implementations are correct, is_trivially_destructible_v may be true or false)
  • Implementation defect in every implementation (implementation should have non-trivial destructor for std::latch)
  • Standard defect (Standard shouldn’t have specified std::latch to have non-trivial destructor)

?

Answer

This is implementation freedom. The C++ standard defines the class, the implementation of the class is up, well, to the implementation.

There are some classes where the standard explicitly mandates a trivial destructor. For example, if an existing class is trivially destructible then its std::optional also must be trivially destructible. This needs to be spelled out.

Therefore, unless somewhere there is an explicit statement that the class is or is not trivially constructible, then this is up to the implementation (where possible).

Looking at gcc’s header file: it doesn’t merely declare, but it explicitly defines the destructor:

~latch() = default;

Based on that the compiler can work out that the whole thing is trivially destructible.