1

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.

wanghan02
  • 1,227
  • 7
  • 14
  • Yes this class is trivially copyable – Cory Kramer Jun 15 '23 at 12:38
  • @DanielLangr The copy assignment operator is not eligible because the constraint is not satisfied. move assignment operator is not eligible because it's deleted. copy/move constructors are eligible and trivial. It looks like a trivially copyable class to me. – wanghan02 Jun 15 '23 at 13:13

1 Answers1

1

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.

Proof for S<int> being trivially copyable

It'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]).

- [class.prop] §1

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,
  • [...]

- [class.copy.assign] $4

This means that:

  • the copy-assignment operator is user-declared, but not eligible
  • the move-assignment operator is not declared

All of the remaining (eligible) special member functions are trivial, therefore S<int> is trivially copyable.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96