Home > Mobile >  Trivially copyable class - what has changed in C 20?
Trivially copyable class - what has changed in C 20?

Time:07-19

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?

CodePudding user response:

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.

CodePudding user response:

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

Before C 20, a copy/move constructor/assignment operator had to be non-deleted and trivial. Since C 20 it's supposed to be eligible and trivial:

A copy constructor is eligible if

  • it is not deleted, and
  • its associated constraints, if any, are satisfied, and
  • no copy constructor with the same first parameter type is more constrained than it.

Essentially the difference is that post C 20 constructors/assignment operators need to meet the concept requirements, if any.

CodePudding user response:

You only need one.

#include <iostream>
#include <type_traits>

struct S
{
    S() noexcept = default;
    S(S const &) noexcept = default;
    S(S &&) noexcept = delete;
    S &operator =(S const &) noexcept = delete;
    S &operator =(S &&) noexcept = default;
};

int main()
{
    std::boolalpha(std::cout);
    std::cout << "Trivially_copyable: " << std::is_trivially_copyable_v<S> << '\n';
    return 0;
}

output for me, at Compiler Explorer:

Trivially_copyable: true

I tried this for several different compilers and C 17 and C 20 just in case, with the same output.

The wording changed to "eligible" between C 17 and C 20 because of the introduction of concepts. Look at the definition of an "eligible" copy constructor:

A copy constructor is eligible if

  • it is not deleted, and
  • its associated constraints, if any, are satisfied, and
  • no copy constructor with the same first parameter type is more constrained than it.

C 20 constraints let the compiler make a choice between overloads with the same parameters. So in order for a copy constructor to be eligible, the "most constrained" copy constructor has to be deleted or trivial.

I've tried to contrive a template class that is trivially copyable for some specializations but not others, but with no success yet.

  • Related