Example code as below or on godbolt. clang 16/trunk believes S<int>
is not a trivially_copyable
class. clang 15, gcc trunk and MSVC believe otherwise.
#include <type_traits>
template<typename T>
struct S {
T m_t;
S(S const&) = default;
S(S&&) = default;
S& operator=(S const&) requires (!std::is_integral<T>::value) = default;
~S() = default;
};
// next five assertions pass for all compilers
static_assert(std::is_trivially_destructible<S<int>>::value);
static_assert(std::is_trivially_copy_constructible<S<int>>::value);
static_assert(std::is_trivially_move_constructible<S<int>>::value);
static_assert(!std::is_copy_assignable<S<int>>::value);
static_assert(!std::is_move_assignable<S<int>>::value);
// compiles with gcc trunk, MSVC and clang 15, fails with clang 16/trunk
static_assert(std::is_trivially_copyable<S<int>>::value);
According to the standard class.prop:
A trivially copyable class is a class:
that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator ([special],
[class.copy.ctor], [class.copy.assign]),where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and
that has a trivial, non-deleted destructor ([class.dtor]).
S<int>
has trivial copy/move constructor and trivial destructor. Its copy/move assignment operators are not eligible. I would agree with gcc/MSVC/clang15 on this. Is clang 16/trunk wrong on this one or am I missing something?
Edit: This is a confirmed clang bug.
Yes, S<int>
is trivially copyable. It looks like you've discovered a clang bug. I was unable to find it in:
So this may be a new regression.
S<int>
being trivially copyableIt's worth examining what elegible means:
An eligible special member function is a special member function for which:
- the function is not deleted,
- the associated constraints ([temp.constr]), if any, are satisfied, and
- no special member function of the same kind is more constrained ([temp.constr.order]).
S& operator=(S const&) requires (!std::is_integral<T>::value) = default
This copy assignment operator is user-declared (and not user-provided) and its constraints aren't satisfied, so it is not eligible. Furthermore:
If the definition of a class
X
does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if
X
does not have a user-declared copy constructor,- [...]
This means that:
All of the remaining (eligible) special member functions are trivial, therefore S<int>
is trivially copyable.