Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How am I misunderstanding the C++ standard regarding "trivially copyable"?

The static assert in the code below fails.

Of course, I know that this is because of the user-provided move constructor in Bar.

#include <type_traits>

using namespace std;

struct Bar
{
public:
    Bar()               = default;
    Bar(const Bar&)     = default;
    ~Bar()              = default;

    Bar(Bar&&)
    {
    }
};

struct Foo
{
    Bar b;

    Foo()                           = default;
   ~Foo()                           = default;
    Foo(const Foo&)                 = default;
    Foo(Foo&&)                      = delete;
    Foo & operator= (Foo && )       = delete;
    Foo & operator= (const Foo & )  = delete;
};


static_assert(is_trivially_copyable_v<Foo>);  // Fails

What I don't understand is how to interpret the standard regarding trivial copyability. From the C++ 17 standard:

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.

It seems to me the Foo meets these criteria:

6.1: They're all deleted except the copy constructor which is trivial. Bar also has a trivial copy constuctor, so that should ensure the Foo copy contructor is truly trivial.

6.2: The defaulted copy constructor

6.3: Foo destructor is defaulted and so is the Bar one, so the destructor should be trivial.

What am I not getting?

By the way I'm not actually trying to do anything apart from better understand the standard.

like image 677
AILien Avatar asked May 26 '21 10:05

AILien


People also ask

What does trivially copyable mean C++?

A trivially copyable class is a class (defined with class, struct or union) that: uses the implicitly defined copy and move constructors, copy and move assignments, and destructor. has no virtual members. its base class and non-static data members (if any) are themselves also trivially copyable types.

Is trivially copyable?

A trivially copyable class is a class that: has no non-trivial copy constructors, has no non-trivial move constructors, has no non-trivial copy assignment operators, has no non-trivial move assignment operators, and has a trivial destructor.

Is STD pair trivially copyable?

std::pair has a non-trivial copy-assignment and move-assignment operator. This prevents it from being trivially copyable. Since C++17, if one of the two contained types is not assignable, then the copy/move assignment operator is defined as deleted, which lifts this restriction on being trivially copyable.

Is STD array trivially copyable?

std::array however, has a static size set at compile time. It does not have internal pointers and can therefore be copied simply by using memcpy . It therefore is trivial to copy.


Video Answer


1 Answers

Except I miss something fundamentally further on here, I think there's currently no actual vague statement about this within the standard, it's actually quite straight forward on my opinion and that means, you're right. Your class Foo should be trivially copyable.

Beginning at top-level for your class Foo:

class6 says:

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.

Points 2) and 3) are trivial and fulfilled.

Point 1): The only constructor of relevance here, that is not deleted, is the copy constructor. So is it trivial?

class.copy#ctor-11 answers that:

11

A copy/move constructor for class X is trivial if it is not user-provided and if:

(11.1) class X has no virtual functions and no virtual base classes, and

(11.2) the constructor selected to copy/move each direct base class subobject is trivial, and

(11.3) for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;

otherwise the copy/move constructor is non-trivial.

Again, points 1) and 2) are trivial and fulfilled. For point 3, the standard uses the terminus "copy/move", not copy and move, emphasizing the dual distinguished orthogonal(!) nature here (common and straight forward scheme within the drafts). Since there's a trivial non-deleted copy constructor available for Bar, there's no chance for any trouble here reading the standard strictly, since how should the selected copy constructor for the bar member be the user-defined move constructor (see memberwise copy for the default copy constructor)? The "decay" to the copy-construction way if no selectable move constructor could be found the other way around would be plausible though, but that's not the case here.

like image 142
Secundi Avatar answered Sep 21 '22 19:09

Secundi