A common pattern in C++ is to make the copy constructor private:
class A
{
public:
// ...
private:
A(const A&);
};
But will the following code then compile (in C++11/14):
A f();
auto a = f();
The standard contains information about automatically generating move constructors. I neither have access to the standard nor a compiler which actually generates move constructors. My question is: do I have to write
class A
{
public:
// ...
private:
A(const A&);
A(const A&&);
};
to prevent moving as well (and operators= analogously)?
But will the following code then compile (in C++11/14):
No, it will not. The presence of a user-declared copy constructor should inhibit the implicit generation of a move constructor. Per paragraph 12.8/9 of the C++11 Standard:
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.
Related
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
Say a class
class Piece {} ;
If I'm correct that should be equivalent to :
class Piece {
//C++ 03
Piece (); //default constructor
Piece( const Piece&); //copy constructor
Piece& operator=(const Piece&); //copy assignment operator
~Piece(); //destructor
//Since C++ 11
Piece(Piece&&); //move constructor
Piece& operator=(Piece&&); //move assignment operator
};
So what can I say about these ?
a)
class Pawn{
~Pawn() {}// Only destructor
};
b)
class Bishop{
Bishop(Bishop&& ) {}// Only move constructor
};
c)
class Knight{
Knight(Knight&&, int =0) {} // move constructor, when no second arg present
};
d)
class Rook {
Rook(const Rook& ) {}// Only copy constructor
};
e)
class King{
King& operator=(const King&) = delete;
};
My per my understanding compiler will generate for :
a) Default Constructor, Copy Constructor, Copy Assignment operator , ( move constructor/assignment operator ? )
b) Destructor
c) Destructor
d) Copy Assignment operator and Destructor ( move constructor/assignment operator ? )
e) Default Constructor, Copy Constructor, Destructor, ( move constructor/assignment operator ? )
Am I correct or missing something here ?
Basically is C++11 has any new rules for generating functions, when not provided by user ?
I'll leave out some irrelevant points here, e.g. about unions, base classes, brace-or-equal-initializers etc. If your classes have any members, base classes, ... then the answer will differ. For example, if you have a const member, an implicitly declared assignment operator would be defined as deleted.
default constructor
[class.ctor]/5
A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted. An implicitly-declared default constructor is an inline public member of its class. A defaulted default constructor for class X is defined as deleted if [... lots of points irrelevant here].
So in cases a) and e) [without any user-declared ctor], a default ctor is declared as defaulted.
default destructor
[class.dtor]
4 If a class has no user-declared destructor, a destructor is implicitly declared as defaulted. 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 [... lots of points irrelevant here]
So in all cases but a) [with a user-declared dtor], a default dtor is implicitly declared and implicitly defined if odr-used.
As per [class.copy]/2+3, a copy-ctor and move-ctor may have additional parameters, if those have default arguments.
copy-constructor
A copy-ctor is declared implicitly if there's no user-defined copy-ctor (a ctor template is never a copy-ctor). [class.copy]/7
If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted. The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
That is, in all cases but d) [with a user-declared copy-ctor], a copy-ctor is declared implicitly. In cases b) and c) [with a user-provided move ctor], the copy-ctor is defined as deleted. For a) [user-declared dtor] and e) [user-declared copy-assignment op], it may be defined as defaulted, but that's deprecated.
move-constructor
The move-ctor won't even be declared in these cases [class.copy]/9
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.
There are again quite some cases where it would be defined as deleted, but they don't apply here.
Therefore, the move-ctor is not declared in any of the cases.
copy-assignment operator
In [class.copy]/18:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted. The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor.
It is defined as deleted in some cases, see [class.copy]/23, but they don't apply here.
The copy-assignment op is declared in all cases but e) [user-declared copy-assignment op]. It is defined as deleted in b) and c) [both: user-declared move ctor]; and it may be defined as defaulted in a) [user-declared dtor] and d) [user-declared copy-ctor]. Note the parallel to the copy-ctor.
move-assignment operator
Similar to the move-ctor, the move-assignment op is not even declared if either [class.copy]/20:
X does not have a user-declared copy constructor,
X does not have a user-declared move constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared destructor, and
the move assignment operator would not be implicitly defined as deleted.
It is defined as deleted in some cases, see [class.copy]/23 (same paragraph as for the copy-ctor), but they don't apply here.
A move-assignment-op is declared implicitly and defined as defaulted in none of the cases.
So looking at some post and online tutorials, I concluded this :
Generated functions :-
C++ 03:
1) Default Constructor (generated only if no constructor is declared by user)
2) Copy Constructor (generated only if No. 5,6 declared by user)
3) Copy Assignment operator (generated only if 5,6 not declared by user)
4) Destructor
Since C++ 11:
5) Move Constructor (generated only if 2,3,4,6 not declared by user)
6) Move Assignment Operator (generated only if 2,3,4,5 not declared by user)
So, for
a)
class Pawn{ //1, 2, 3
~Pawn() {}// Only destructor
};
b)
class Bishop{ //4
Bishop(Bishop&& ) {}
};
c)
class Knight{ //4
Knight(Knight&&, int =0) {}
};
d)
class Rook { //3, 4
Rook(const Rook& ) {}
};
e)
class King{ //1, 2, 4
King& operator=(const King&) = delete;
};
Edit : As per DyP comment :-
In C++11,
For case a), 2 and 3 are deprecated.
For case d), 3 is deprecated.
For case e), 2 is deprecated.
I have the following sample:
#include <vector>
class noncopyable {
protected:
noncopyable() {}
~noncopyable() {}
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
noncopyable(noncopyable&&) = default;
noncopyable& operator=(noncopyable&&) = default;
};
class C1 : private noncopyable {
public:
C1() { }
~C1() { }
};
int main() {
std::vector<C1> v;
v.emplace_back();
return 0;
}
I thought it should work since C1 should be movable since it's base class is and it has no data members. Instead, I got an an error (using clang++):
error: call to implicitly-deleted copy constructor of 'C1'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
.
.
.
note: in instantiation of function template specialization 'std::vector<C1, std::allocator<C1> >::emplace_back<>' requested here
v.emplace_back();
^
note: copy constructor of 'C1' is implicitly deleted because base class 'noncopyable' has a deleted copy constructor
class C1 : private noncopyable {
^
note: 'noncopyable' has been explicitly marked deleted here
noncopyable(const noncopyable&) = delete;
Doing a little research (http://en.cppreference.com/w/cpp/language/move_constructor) revealed that if there is a user-defined destructor then no implicit move-constructor will be defined. This seemes to be the problem here, since C1 has a destructor, the move-constructor does not get defined. Sure enough, if I either remove the destructor or add C1(C1&&) = default; to C1 then it works.
So far so good.
The problem was that the error message didn't mention ~C1() or the move-constructor. It said it was trying to call the copy constructor, which was deleted in the base class. So I tried changing the deleteed functions in noncopyable to be defaulted instead, and (surprise!), that also solved the error.
So my question is, what does this last thing have to do with the error or it's correction? If there is a destructor, what is the difference if the base class has the copy-constructor or not?
You don't need vector, a simpler example would be just:
C1 a;
C1 b(std::move(a)); // error: C1's copy constructor is deleted
From [class.copy]:
If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if
(9.1) — X does not have a user-declared copy constructor,
(9.2) — X does not have a user-declared copy assignment operator,
(9.3) — X does not have a user-declared move assignment operator, and
(9.4) — X does not have a user-declared destructor.
C1 has a user-declared destructor, therefore it does not have a move constructor. C1 does however have an implicitly declared copy constructor
If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy
constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
The full set of constructors on C1, explicit and implicit, looks like:
C1();
C1(C1 const& ) = default; // but also delete
~C1();
So trying to construct a C1 from an rvalue of type C1 will match that implicitly declared copy constructor as the best match (nothing else is viable), but that constructor is deleted because noncopyable's copy constructor is deleted, so the entire expression is ill-formed.
That's why the error message mentions the constructor. That move-construction is ill-formed because the best match for that move-construction is the copy constructor is ill-formed. It can't mention the move constructor because there is no move constructor, and the destructor isn't relevant to the expression at hand. When you changed the base class to be copyable, now C1 also becomes copyable - so no error. There's still no move constructor, it's just that now there's a viable candidate for move construction.
Say a class
class Piece {} ;
If I'm correct that should be equivalent to :
class Piece {
//C++ 03
Piece (); //default constructor
Piece( const Piece&); //copy constructor
Piece& operator=(const Piece&); //copy assignment operator
~Piece(); //destructor
//Since C++ 11
Piece(Piece&&); //move constructor
Piece& operator=(Piece&&); //move assignment operator
};
So what can I say about these ?
a)
class Pawn{
~Pawn() {}// Only destructor
};
b)
class Bishop{
Bishop(Bishop&& ) {}// Only move constructor
};
c)
class Knight{
Knight(Knight&&, int =0) {} // move constructor, when no second arg present
};
d)
class Rook {
Rook(const Rook& ) {}// Only copy constructor
};
e)
class King{
King& operator=(const King&) = delete;
};
My per my understanding compiler will generate for :
a) Default Constructor, Copy Constructor, Copy Assignment operator , ( move constructor/assignment operator ? )
b) Destructor
c) Destructor
d) Copy Assignment operator and Destructor ( move constructor/assignment operator ? )
e) Default Constructor, Copy Constructor, Destructor, ( move constructor/assignment operator ? )
Am I correct or missing something here ?
Basically is C++11 has any new rules for generating functions, when not provided by user ?
I'll leave out some irrelevant points here, e.g. about unions, base classes, brace-or-equal-initializers etc. If your classes have any members, base classes, ... then the answer will differ. For example, if you have a const member, an implicitly declared assignment operator would be defined as deleted.
default constructor
[class.ctor]/5
A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted. An implicitly-declared default constructor is an inline public member of its class. A defaulted default constructor for class X is defined as deleted if [... lots of points irrelevant here].
So in cases a) and e) [without any user-declared ctor], a default ctor is declared as defaulted.
default destructor
[class.dtor]
4 If a class has no user-declared destructor, a destructor is implicitly declared as defaulted. 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 [... lots of points irrelevant here]
So in all cases but a) [with a user-declared dtor], a default dtor is implicitly declared and implicitly defined if odr-used.
As per [class.copy]/2+3, a copy-ctor and move-ctor may have additional parameters, if those have default arguments.
copy-constructor
A copy-ctor is declared implicitly if there's no user-defined copy-ctor (a ctor template is never a copy-ctor). [class.copy]/7
If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted. The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
That is, in all cases but d) [with a user-declared copy-ctor], a copy-ctor is declared implicitly. In cases b) and c) [with a user-provided move ctor], the copy-ctor is defined as deleted. For a) [user-declared dtor] and e) [user-declared copy-assignment op], it may be defined as defaulted, but that's deprecated.
move-constructor
The move-ctor won't even be declared in these cases [class.copy]/9
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.
There are again quite some cases where it would be defined as deleted, but they don't apply here.
Therefore, the move-ctor is not declared in any of the cases.
copy-assignment operator
In [class.copy]/18:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted. The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor.
It is defined as deleted in some cases, see [class.copy]/23, but they don't apply here.
The copy-assignment op is declared in all cases but e) [user-declared copy-assignment op]. It is defined as deleted in b) and c) [both: user-declared move ctor]; and it may be defined as defaulted in a) [user-declared dtor] and d) [user-declared copy-ctor]. Note the parallel to the copy-ctor.
move-assignment operator
Similar to the move-ctor, the move-assignment op is not even declared if either [class.copy]/20:
X does not have a user-declared copy constructor,
X does not have a user-declared move constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared destructor, and
the move assignment operator would not be implicitly defined as deleted.
It is defined as deleted in some cases, see [class.copy]/23 (same paragraph as for the copy-ctor), but they don't apply here.
A move-assignment-op is declared implicitly and defined as defaulted in none of the cases.
So looking at some post and online tutorials, I concluded this :
Generated functions :-
C++ 03:
1) Default Constructor (generated only if no constructor is declared by user)
2) Copy Constructor (generated only if No. 5,6 declared by user)
3) Copy Assignment operator (generated only if 5,6 not declared by user)
4) Destructor
Since C++ 11:
5) Move Constructor (generated only if 2,3,4,6 not declared by user)
6) Move Assignment Operator (generated only if 2,3,4,5 not declared by user)
So, for
a)
class Pawn{ //1, 2, 3
~Pawn() {}// Only destructor
};
b)
class Bishop{ //4
Bishop(Bishop&& ) {}
};
c)
class Knight{ //4
Knight(Knight&&, int =0) {}
};
d)
class Rook { //3, 4
Rook(const Rook& ) {}
};
e)
class King{ //1, 2, 4
King& operator=(const King&) = delete;
};
Edit : As per DyP comment :-
In C++11,
For case a), 2 and 3 are deprecated.
For case d), 3 is deprecated.
For case e), 2 is deprecated.
I guess it is so, but I am looking for C++11 language lawyers to confirm my impression. Is it true that the following class
struct X{
X(){}
X(X const&)=default;
};
will not be automatically move-enabled, i.e., getting X(X&&) and operator=(X&&), because its copy constructor is "user-declared", even though it looks equivalent to
struct X{
};
which will get both X(X const&) and X(X&&) etc., implicitely declared and (trivially) defined on use.
From the standard:
8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]
4 - [...] A special member function is user-provided if it is user-declared and not explicitly
defaulted or deleted on its first declaration. [...]
An explicit default can be combined with its declaration, or it can be separate:
struct S {
S();
};
S::S() = default;
In either case its (first) declaration makes it user-declared.
Yes, your defaulted copy assign operator precludes the implicit move ctor.
BTW putting =default is actually a definition. I remember trying to implement a pimpl idiom with std::unique_ptr and having to remove =default from headers and putting them in the implementation file because the destructor for unique_ptr needed the definition of the class it is trying to clean up.
A defaulted copy constructor is indeed "user-declared"; I think the addition of default was in fact the reason why they changed the term from "user-defined" to "user-declared".
That is correct, §12.8 sets the conditions when a move constructor gets implicitly declared and the presence of a user-declared copy constructor precludes that. You cannot have
user-declared copy constructor
user-declared copy assignment operator
user-declared move-assignment operator
user-declared destructor