Is a class with deleted copy-constructor trivially copyable? - c++

Is this class:
class A {
public:
A() = default;
A(const A&) = delete;
};
trivially copyable? (At least clang seems to think so (live))
In particular, would
A a,b;
std::memcpy(&a, &b, sizeof(A));
invoke undefined behavior?
Context: This answer [deleted because proven wrong] plus its comment tree.

Update: The proposed resolution of CWG 1734, currently in "ready" status, would modify [class]/p6 to read:
A trivially copyable class is a class:
where each copy constructor, move constructor, copy assignment operator, and move assignment operator (12.8 [class.copy], 13.5.3
[over.ass]) is either deleted or trivial,
that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator,
and
that has a trivial, non-deleted destructor (12.4 [class.dtor]).
This renders classes like
struct B {
B() = default;
B(const B&) = delete;
B& operator=(const B&) = delete;
};
no longer trivially copyable. (Classes of this sort include synchronization primitives like std::atomic<T> and std::mutex.)
However, the A in the OP has a implicitly declared, non-deleted copy assignment operator that is trivial, so it remains trivially copyable.
The original answer for the pre-CWG1734 situation is preserved below for reference.
Yes, somewhat counterintuitively, it is trivially copyable. [class]/p6:
A trivially copyable class is a class that:
has no non-trivial copy constructors (12.8),
has no non-trivial move constructors (12.8),
has no non-trivial copy assignment operators (13.5.3, 12.8),
has no non-trivial move assignment operators (13.5.3, 12.8), and
has a trivial destructor (12.4).
[class.copy]/p12:
A copy/move constructor for class X is trivial if it is not
user-provided, its parameter-type-list is equivalent to the
parameter-type-list of an implicit declaration, and if
class X has no virtual functions (10.3) and no virtual base classes (10.1), and
class X has no non-static data members of volatile-qualified type, and
the constructor selected to copy/move each direct base class subobject is trivial, and
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;
Similarly ([class.copy]/p25):
A copy/move assignment operator for class X is trivial if it is not
user-provided, its parameter-type-list is equivalent to the
parameter-type-list of an implicit declaration, and if
class X has no virtual functions (10.3) and no virtual base classes (10.1), and
class X has no non-static data members of volatile-qualified type, and
the assignment operator selected to copy/move each direct base class subobject is trivial, and
for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is
trivial;
[class.dtor]/p5:
A destructor is trivial if it is not user-provided and if:
the destructor is not virtual,
all of the direct base classes of its class have trivial destructors, and
for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial
destructor.
[dcl.fct.def.default]/p5:
A function is user-provided if it is user-declared and not explicitly
defaulted or deleted on its first declaration.
Indeed, this has been a source of problems for the committee itself, because under the current definition atomic<T> (along with mutexes and condition variables) would be trivially copyable. (And obviously, allowing someone to memcpy over an atomic or a mutex without invoking UB would be ... let's just say seriously problematic.) See also N4460.

Related

C++ primer 5 ed. Virtual destructor and move semantics

In C++ primer 5 ed. it is said:
"The fact that a base class needs a virtual destructor has an important indirect impact on the definition of base and derived classes: If a class defines a destructor—even if it uses = default to use the synthesized version—the compiler will not synthesize a move operator for that class (§13.6.2, p. 537)."
So for more understanding I've tried this:
class A
{
public:
A(){cout << "A::A()\n";}
virtual ~A(){cout << "A::~A()\n";}
A& operator = (const A&) {cout << "A::=\n"; return *this;};
A& operator = (A&&) = default;
};
int main()
{
A a;
A a2;
a2 = std::move(a); // why copy-assignment operator is not called here. Normally a will be "moved" through copy-ctor (moving through working copy-ctor)
std::cout << std::endl;
}
So according to the text above in the book class A has a virtual destructor thus the compiler won't synthesize a move-ctor/assignment operations But in main I've tried to move an object of type class A so normally move here uses copy-assignment instead of move-assignment but copy-assignment is not called here?!
So when a vairtual dtor causes a default operation be not synthesized? Thank you!
Your textbook is right, the compiler won't automatically synthesize a move operator. In the code sample you force the compiler to generate one by setting it to default, this can only fail if the move operator is implicitly deleted. Which is something else than it being implicitly declared.
Criteria for implicit deletion:
A has a non-static data member that is const
A has a non-static data member of a reference type
A has a non-static data member that cannot be move-assigned (has deleted, inaccessible, or ambiguous move assignment operator)
A has direct or virtual base class that cannot be move-assigned (has deleted, inaccessible, or ambiguous move assignment operator)
A has a non-static data member or a direct or virtual base without a move assignment operator that is not trivially copyable
A has a direct or indirect virtual base class
Criteria for implicit declaration:
there are no user-declared copy constructors;
there are no user-declared move constructors;
there are no user-declared copy assignment operators;
there are no user-declared destructors;
the implicitly-declared move assignment operator would not be defined as deleted
source

Need for declare destructor as default

According to these guidelines:
If the default destructor is needed, but its generation has been
suppressed (e.g., by defining a move constructor), use =default.
I can't imagine when code would be ill-formed without explicit default destructor in class which has move constructor.
Can somebody show me example confirms quote above?
struct S {
S() {};
S( S&& ) {}; // move ctor
};
int main() {
S s; // there is no need to declare dtor explicitly =default
}
I think it would be some kind of mistake, the implicit declaration of default destructor should have nothing to do with the definition of a move constructor.
From the standard, 12.4$4,5 Destructors [class.dtor]
4 If a class has no user-declared destructor, a destructor is
implicitly declared as defaulted (8.4). An implicitly-declared
destructor is an inline public member of its class.
5 A defaulted destructor for a class X is defined as deleted if:
(5.1) — X is a union-like class that has a variant member with a
non-trivial destructor,
(5.2) — any potentially constructed subobject has class type M (or
array thereof) and M has a deleted destructor or a destructor that is
inaccessible from the defaulted destructor,
(5.3) — or, for a virtual destructor, lookup of the non-array
deallocation function results in an ambiguity or in a function that is
deleted or inaccessible from the defaulted destructor.

Default move constructor in a sub class

In C++11, if the base class has defined its own move (copy) constructor (assignment operator), does its subclass need to define its own move (copy) constructor (assignment operator) in where call the base class's corresponding constructor/operator is called explicitly?
Is it a good idea to define the constructor, destructor, move/copy constructor (assignment operator) clearly every time?
struct Base {
Base() {}
Base(Base&& o);
};
struct Sub : public Base {
Sub(Sub&& o) ; // Need I do it explicitly ? If not,what the compiler will do for me
};
The compiler will generate a default move constructor if you don't specify one in the base class (except some cases, e.g. there's a base class with a deleted move constructor) but you should, in any case, call explicitly the base class' one if you have it:
Sub(Sub&& o) : Base(std::move(o))
According to the standard (N3797) 12.8/9 Copying and moving class objects [class.copy]:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator, and
— X does not have a user-declared destructor.
As such, if your class meets the above requirements then a default move constructor will be implicitly declared for you.
As already being stated, the base-class has no knowledge of any sub-classes. As such, whether you declare a move constructor in one base class has no effect on the implicit generation of a move constructor in its sub-classes.
As far as it concerns whether you should declare explicitly a constructor/destructor etc. of a class, there's this nice article.
No, you don't have. I'll be automatically generated like default/copy constructor.
From this page,
Implicitly-declared move constructor
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
there are no user-declared copy constructors
there are no user-declared copy assignment operators
there are no user-declared move assignment operators
there are no user-declared destructors
(until C++14) the implicitly-declared move constructor is not defined as deleted due to conditions detailed in the next section
then the compiler will declare a move constructor as an inline public member of its class with the signature T::T(T&&).
A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&). If some user-defined move constructors are present, the user may still force the generation of the implicitly declared move constructor with the keyword default.
Your struct Sub has no user-declared copy constructors, copy assignment operators, move assignment operators or destructors.
And,
Trivial move constructor
The move constructor for class T is trivial if all of the following is true:
It is not user-provided (meaning, it is implicitly-defined or defaulted), and if it is defaulted, its signature is the same as implicitly-defined
T has no virtual member functions
T has no virtual base classes
The move constructor selected for every direct base of T is trivial
The move constructor selected for every non-static class type (or array of class type) member of T is trivial
T has no non-static data members of volatile-qualified type
(since C++14)
A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially movable.
Implicitly-defined move constructor
If the implicitly-declared move constructor is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler. For union types, the implicitly-defined move constructor copies the object representation (as by std::memmove). For non-union class types (class and struct), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument.
The move constructor of Base is not trivial (it's user-defined). So, the implicitly-defined move constructor of Sub will work as "the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument."

union can't contain an object that has a virtual function

Code:
struct A{
int a;
virtual void f(){}
};
union B{
A ob;
};
Compile-time Error:
C:\to\main.cpp|9|error: member 'A B::ob' with constructor not allowed in union|
C:\to\main.cpp|9|error: member 'A B::ob' with copy assignment operator not allowed in union|
||=== Build finished: 2 errors, 0 warnings ===|
c++03 Standard:
An object of a class with a non-trivial constructor (12.1), a
non-trivial copy constructor (12.8), a non-trivial destructor (12.4),
or a non-trivial copy assignment operator (13.5.3, 12.8) cannot be a
member of a union, nor can an array of such objects. If a union
contains a static data member, or a member of reference type, the
program is ill-formed.
The standard doesn't say anything about an object of a class with a virtual function, and from the error, the compiler complain about constructor and copy-assignment operator which I didn't use. so is this a compiler bug ? Im using gcc .
The implicitly declared default constructor, copy constructor, and copy assignment operator for that struct are non-trivial because it has a virtual function, so you've broken those requirements.
A constructor is trivial if it is an implicitly-declared default constructor and if:
its class has no virtual functions (10.3) and no virtual base classes (10.1), and
[...]
A copy constructor for class X is trivial if it is implicitly declared and if
class X has no virtual functions (10.3) and no virtual base classes (10.1), and
[...]
A copy assignment operator for class X is trivial if it is implicitly declared and if
class X has no virtual functions (10.3) and no virtual base classes (10.1), and
[...]
The C++11 quote is similar (it just includes move constructors and assignment operators) but C++11 does not have the same requirement on members of unions.
Use C++11 if you can, it doesn't contain such constraint

Can a move constructor be implicit?

Consider the following class:
class A
{
public:
std::string field_a;
std::string field_b;
}
Now consider the following copy construction:
A a1(a2);
The copy construction will adequately copy A despite the lack of of an explicit copy constructor because the copy constructors for std::string will be called by the compiler generated implicit copy constructor.
What I wish to know is, is the same true for move construction?
EDIT: Testing here shows that:
A a2(std::move(a1));
Will actually result in a copy construction, unless the specific move constructor:
A( A && other ) : a(std::move(other.a)) {}
Is defined.
EDIT EDIT
I pinged Stephan T Lavavej and asked him why VC 2012 doesn't seem to follow what draft 12.8 states regarding implicit move constructor generation. He was kind enough to explain:
It's more of a "feature not yet implemented" than a bug. VC currently
implements what I refer to as rvalue references v2.0, where move
ctors/assigns are never implicitly generated and never affect the
implicit generation of copy ctors/assigns. C++11 specifies rvalue
references v3.0, which are the rules you're looking at.
Yes, from the C++11 draft, 12.8:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared
as defaulted if and only if
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator,
X does not have a user-declared destructor, and
the move constructor would not be implicitly defined as deleted.
The last condition is specified with more detail later:
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:
a variant member with a non-trivial corresponding constructor and X is a union-like class,
a non-static data member of class type M (or array thereof) that cannot be copied/moved because
overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a
function that is deleted or inaccessible from the defaulted constructor,
a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as
applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or
inaccessible from the defaulted constructor,
any direct or virtual base class or non-static data member of a type with a destructor that is deleted
or inaccessible from the defaulted constructor,
for the copy constructor, a non-static data member of rvalue reference type, or
for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
Plainly speaking, the move constructor will be implicitly declared if:
The class does not have user-declared any of the other special member functions.
The move constructor can be sensibly implemented by moving all its members and bases.
Your class obviously complies with these conditions.
The compiler synthesizes a move constructor if it can and if there is no user-defined copy constructor. The restriction that no move constructor is synthesized if there is copy constructor is intended to avoid breaking existing code. Of course, all members need to be movable. The exact rules are a bit more involved.