Consider the following move-only type:
struct MoveOnly
{
MoveOnly() = default;
~MoveOnly() = default;
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) noexcept = default;
MoveOnly& operator=(MoveOnly&&) noexcept = default;
};
Both GCC and Clang agree on:
static_assert(not std::is_trivially_copy_constructible_v<MoveOnly>);
static_assert(not std::is_copy_constructible_v<MoveOnly>);
static_assert( std::is_trivially_copyable_v<MoveOnly>);
Now consider this wrapper type that propagates trivial copy construcibility:
template <typename T>
struct Wrapper
{
Wrapper(const Wrapper&)
requires(!std::is_trivially_copy_constructible_v<T> && std::is_copy_constructible_v<T>)
{
}
Wrapper(const Wrapper&)
requires(std::is_trivially_copy_constructible_v<T>) = default;
};
Both GCC and Clang agree on:
static_assert(not std::is_trivially_copy_constructible_v<Wrapper<MoveOnly>>);
static_assert(not std::is_copy_constructible_v<Wrapper<MoveOnly>>);
However, GCC believes the following is true, while Clang believes it's false:
static_assert( std::is_trivially_copyable_v<Wrapper<MoveOnly>>);
// passes on GCC, fails on Clang
What compiler is correct here and why?
The type Wrapper<MoveOnly>
is trivially-copyable.
Wrapper<MoveOnly>
has no eligible copy constructor because both of your declared ones have constraints that are not satisfied.
It also has no move constructor, move assignment operator or additional implicitly-declared copy constructor because you declared copy constructors yourself.
It does however have one eligible copy assignment operator. This one is implicitly-declared because you didn't declare any copy assignment operator yourself. It is also defaulted, not defined as deleted and trivial. (However, this implicit declaration of the copy assignment operator if a user-declared copy constructor is present has been deprecated since C++11.)
So there exists at least one eligible copy/move constructor/assignment operator and all of these are trivial. Wrapper<MoveOnly>
also has a trivial, non-deleted destructor. So all requirements for trivial-copyability are satisfied.
(If you search for is:issue is:open trivially-copyable label:clang:frontend
on LLVM's issue tracker you'll find multiple open issues where Clang currently incorrectly decides trivial-copyability and there are open CWG issues as well.)