Closed
Description
The title section currently reads:
A defaulted copy/move assignment operator for class X is defined as deleted if X has:
- a variant member with a non-trivial corresponding assignment operator and X is a union-like class, or
- a non-static data member of const non-class type (or array thereof), or
- a non-static data member of reference type, or
- a direct non-static data member of class type M (or array thereof) or a direct base class M that cannot be copied/moved because overload resolution ([over.match]), as applied to find M's corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator.
What is the "corresponding assignment operator"? Consider the following example (from @foonathan):
#include <type_traits>
//=== the setup ===//
// This class is weird, but Microsoft's std::pair works the same.
struct foo
{
// foo has a trivial default constructor, copy constructor, move constructor and destructor.
foo() = default;
foo(const foo&) = default;
foo(foo&&) = default;
~foo() = default;
// For the compiler, this is a copy assignment operator.
// http://eel.is/c++draft/class.copy.assign#1
foo& operator=(const volatile foo&) = delete;
// For the compiler, this is not a copy assignment operator.
// This is just a weird operator overload.
template <int Dummy = 0>
foo& operator=(const foo&) // non-trivial
{
return *this;
}
// For the compiler, this is not a move assignment operator.
// This is just a weird operator overload.
template <int Dummy = 0>
foo& operator=(foo&&) // non-trivial
{
return *this;
}
};
template <typename T>
struct my_optional
{
union
{
char empty;
T value;
};
};
static_assert(!std::is_copy_assignable_v<my_optional<foo>>);
All compilers agree that my_optional<foo>
is not copy assignable, because copy assignment would be non-trivial. But the variant member T value
here does have a trivial copy assignment operator (the "corresponding assignment operator") -- but it's not the one that would be used when performing copy assignment.
We need to do the same "overload resolution ([over.match]), as applied to find M's corresponding assignment operator" thing from the 4th bullet point to also handle the 1st bullet point cases.