Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[class.copy.assign]/7 and "corresponding" assignment operator in a union-like class #4536

Closed
brevzin opened this issue Mar 1, 2021 · 2 comments · Fixed by #6328
Closed
Labels
cwg Issue must be reviewed by CWG.

Comments

@brevzin
Copy link
Contributor

brevzin commented Mar 1, 2021

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.

@jensmaurer
Copy link
Member

jensmaurer commented Mar 1, 2021

This doesn't look editorial to me at all. Please file a CWG issue.

@jensmaurer jensmaurer added the cwg Issue must be reviewed by CWG. label Mar 1, 2021
@frederick-vs-ja
Copy link
Contributor

Is this covered by CWG1353? (Fixed by #6328 (edfbb70) if so.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cwg Issue must be reviewed by CWG.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants