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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With