Search code examples
c++c++20trivially-copyable

Trivially copyable class - what has changed in C++20?


The standard says

A trivially copyable class is a class:

(1.1) that has at least one eligible copy constructor, move constructor, copy assignment operator, or move assignment operator ([special], [class.copy.ctor], [class.copy.assign]),

(1.2) where each eligible copy constructor, move constructor, copy assignment operator, and move assignment operator is trivial, and

(1.3) that has a trivial, non-deleted destructor ([class.dtor]).

Now, I am not completely sure what it means. Does it mean that it is enough to have one of these? For example a class with trivial copy constructor and copy assignment operator and explicitly deleted move constructor and move assignment operator is trivially copyable, or it means that I have non-deleted "big 6" and every one of them is trivial?

If I read this literally, having just one constructor or assignment operator should be enough. It was the case before c++20 according to cppreference. If nothing has changed (namely I still can have deleted assignment operators or constructors) why was the wording changed? What is the difference between pre c++20 and C++20 standard meaning?

Update

The experiments (such as the one in the answer by Spencer) show that my guess was correct. What I do not understand - why change the wording in C++20 standard. Has anything actually changed?

A link to c++17 definition.

In c++17 the definition was:

  1. A trivially copyable class is a class:

(6.1) where each copy constructor, move constructor, copy assignment operator, and move assignment operator ([class.copy], [over.ass]) is either deleted or trivial,

(6.2) that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator, and

(6.3) that has a trivial, non-deleted destructor.

Are there any subtle differences between old definition and the new one?


Solution

  • What is the difference between pre c++20 and C++20 standard meaning?

    For pre-C++20 classes, none. For post-C++20 classes, the difference is constraints.

    The old wording talked about special member functions being "non-deleted". The new wording talks about them being "eligible." Eligible is defined in [special]/6:

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

    You can think of it as the C++17 rule only had the first bullet while the C++20 rule adds the next two bullets (C++17 didn't have constraints so these latter two bullets are trivially satisfied). The reason for this can be found in the paper that added this wording, P0848 (Conditionally Trivial Special Member Functions) (my paper).

    The goal is for this to do the right thing, which is that optional<int> is trivially copyable, optional<string> is copyable but not trivially copyable, and optional<unique_ptr<int>> isn't copyable:

    template <class T>
    struct optional {
        // #1
        optional(optional const&)
            requires copyable<T> && trivially_copyable<T>
            = default;
    
        // #2
        optional(optional const&) requires copyable<T>;
    };
    

    Before P0848, both #1 and #2 were considered copy constructors of optional<int>, and because #2 isn't trivial, it would fail the requirement that every copy constructor is trivial. But it doesn't matter that #2 isn't trivial, since #1 is really the only important copy constructor - and the new C++20 rules ensure that this is the case.

    Specifically, #2 isn't eligible because #1 is more constrained, so we only care about #1 for determining whether optional<int> is trivially copyable. That copy constructor is trivial, so we're fine. On the other hand, for optional<string>, the associated constraints on #1 aren't satisfied (string isn't trivially copyable), so the eligible copy constructor is #2, which isn't trivial.