I got in a situation which is quite interesting as the code I'm working on compiles even though I'm surprised it does so I would like to ask you for your take.
The situation is this. I have a class with deleted move and copy constructors, which has user-defined assignment operators:
struct A {
A() { }
A(const A&) = delete;
A(A&& ) = delete;
A& operator=(const A& ) { return *this; }
A& operator=(A&& ) { return *this; }
};
And I have another class with A as the only member. In this class I defined the copy constructor but I kept the move constructor as default and defined the assignment operator through a call to the swap function:
class B{
public:
A a;
B()
: a{}
{ }
B(const B&)
: a{}
{ }
B(B&& other) = default;
};
int main() {
B b1;
B b2(std::move(b1)); // compiles??
}
Why does the default move constructor work, considering that it cannot simply call the move or copy constructor A? I am using gcc 4.8.4.
My original answer was wrong, so I'm starting over.
In [class.copy], we have:
A defaulted copy/
move constructor for a class X is defined as deleted (8.4.3) if X has:
— [...]
— a potentially constructed subobject 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,
— [...]
That bullet point applies to B(B&& other) = default;, so that move constructor is defined as deleted. This would seem to break compilation with std::move(), but we also have (via resolution of defect 1402):
A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4). [ Note:
A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the
copy constructor instead. —end note ]
Ignoring is the key. Thus, when we do:
B b1;
B b2(std::move(b1));
Even though the move constructor for B is deleted, this code is well-formed because the move constructor simply doesn't participate in overload resolution and the copy constructor is called instead. Thus, B is MoveConstructible - even if you cannot construct it via its move constructor.
Related
I'm testing c++ class initialization.
class Point
{
private:
int x,y;
public:
Point() = delete;
Point(int a):x(a), y(0) { std::cout << "Conversion" << std::endl;}
Point(const Point&) { std::cout << "Copy constructor" << std::endl;}
//Point(const Point&) = delete;
Point& operator=(const Point&) = delete;
Point(Point&&) = delete;
Point& operator=(Point&&) = delete;
};
int main()
{
Point p1(5); //case1
Point p2 = 5; //case2
return 0;
}
In the above code, I thought "5" will be converted to a temp object by conversion constructor for both case1/2 at first. And then, I expected that copy constructor must be used for initializing of p1 & p2. But, it was not.
When I run this code, I saw just two "Conversion" message in console. No "Copy Constructor" message.
Even though, I deleted all copy constructor, move constructor, copy assignment operator and move assignment operator, this code worked well.
I would appreciate if you let me know what special member function will be used for initializing after creating temp object for "5",
I am using g++ compiler with std=c++17 option.
Case I
In case 1 the converting constructor is used since you have provided a constructor that can convert an int to Point. This is why this constructor is called converting constructor.
Case II
From mandatory copy elison
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:
In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type.
In case 2, even if you delete copy/move constructor only the converting constructor will be used. This is due to mandatory copy elison(in C++17) as quoted above.
I didn't understand this in C++ primer 5 ed. :
For example, assuming Y is a class that defines its own copy constructor but does not also define its own move constructor:
// assume Y is a class that defines its own copy constructor but not a move constructor
struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem; // hasY will have a deleted move constructor
};
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
The compiler can copy objects of type Y but cannot move them. Class hasY explicitly
requested a move constructor, which the compiler is unable to generate. Hence, hasY
will get a deleted move constructor. Had hasY omitted the declaration of its move
constructor, then the compiler would not synthesize the hasY move constructor at all.
The move operations are not synthesized if they would otherwise be defined as
deleted.
So I've defined Y as he said:
struct Y
{
Y() = default;
Y(const Y&){std::cout << "Y's Copy-ctor\n";
Y& operator=(const Y&){std::cout << "Y's copy-assignment operator\n"; return *this;}
};
struct hasY {
hasY() = default;
hasY(hasY&&) noexcept = default;
Y mem; // hasY will have a deleted move constructor
};
In main:
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
But the program works fine and doesn't complain about a deleted move constructor as he said?!
The output:
Y's copy ctor
As you can see the program runs fine with me although I've tested it on many compilers with different versions and with different compiling flags. I guess it is a mistake in the book.
I think class hasY is moveable and its move constructor is not deleted because that move-ctor calls move ctor of class Y which is implicitly not defined (not deleted but not defined) so it falls to use Y's copy-ctor So I think class Y is moveable through its copy constructor. Thus the program pints Y's copy-ctor.
** In cppreference it is said:
The implicitly-declared or defaulted move constructor for class T is defined as deleted if any of the following is true:
T has non-static data members that cannot be moved (have deleted, inaccessible, or ambiguous move constructors);
T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors);
T has direct or virtual base class with a deleted or inaccessible destructor;
T is a union-like class and has a variant member with non-trivial move constructor;
T has a non-static data member or a direct or virtual base without a move constructor that is not trivially copyable.
The deleted implicitly-declared move constructor is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue).
I also have an ambiguity between: "implicitly defined as deleted operation" and "not defined at all"? To me defined as deleted is as if writing:
Y(Y&&) = delete; // generated by the compiler
And not defined at all is the same meaning of the expression.
*** To confirm my saying that class hasY's move constructor is not deleted then here is just a simple:
struct hasY {
hasY() = default;
hasY(hasY&&) noexcept = default;
Y mem; // hasY will have a deleted move constructor
std::unique_ptr<int> upi; // This really causes hasY non-copy-able
};
As you can see I've added a non-copy-able object of type uniqe_ptr to prevent the copy of hasY and the program runs as before with no erorrs which means hasY's move constructor is not deleted.
Here's some code that won't compile because push_back is trying to call the copy constructor in MoveOnlyClass, which is deleted:
class MoveOnlyClass
{
public:
MoveOnlyClass() {};
MoveOnlyClass& operator=(const MoveOnlyClass& other) = delete;
MoveOnlyClass(const MoveOnlyClass& other) = delete;
};
int main()
{
std::vector<MoveOnlyClass> vec;
vec.push_back(std::move(MoveOnlyClass()));
}
Why does this happen? Surely the only thing the vector should be calling is the move constructor. What would be the correct way to move an object into a vector?
Deleting the copy constructor/copy assignment function also implicitly deletes the move-constructor/move assignment function. If you intend to make an object movable but not copyable, you also need to default the move-constructor.
class MoveOnlyClass
{
public:
MoveOnlyClass() {};
MoveOnlyClass& operator=(const MoveOnlyClass& other) = delete;
MoveOnlyClass(const MoveOnlyClass& other) = delete;
MoveOnlyClass& operator=(MoveOnlyClass&& other) = default;
MoveOnlyClass(MoveOnlyClass&& other) = default;
};
//Will now compile as you expect
int main()
{
std::vector<MoveOnlyClass> vec;
vec.push_back(std::move(MoveOnlyClass()));
}
Also, std::move(T()) is redundant; constructing an object in-place like that will already make it an R-value, and using std::move when you don't need to may prevent some kinds of compiler optimizations (like Copy Ellision).
The answer by #Xirema seals the deal about the problem in the code, and explains why that is the case.
I just want to back it up with the appropriate excerpts from the language spec, to make things official. So from [class.copy.ctor¶8]:
(8) 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
(8.1) X does not have a user-declared copy constructor,
For this we should add that declaring as deleted is still declaring. Hence according to this we don't get the implicitly declared move constructor in your case, as we already have a user-declared copy constructor.
Further, under [dcl.fct.def.delete¶3]:
One can make a class uncopyable, i.e., move-only, by using deleted
definitions of the copy constructor and copy assignment operator, and
then providing defaulted definitions of the move constructor and move
assignment operator.
I have some problems understanding when and if the move constructor or move assignment operator are invoked, in particular in the context of a class with constant data member.
Consider the class
template<typename T> class A {
const*T const P ; // constant data member
explicit A(const*T p) : P(p) { std::cerr<<" ctor: P="<<P<<'\n'; }
void test() const { std::cerr" test: P="<<P<<'\n'; }
// move and copy constructors and assignment operators here
};
and the test program
class B {
int X[100];
A<B> get_a() const { return A<B>(this); }
};
int main() {
B b;
A<B> a = b.get_a(); // which operator/ctor is used for '=' here?
a.test();
}
then the results for compilation are different depending on the definitions provided for the move constructor and move assignment operator in class A<>, but also on compiler.
1 without any further declaration in class A<> (as above), both g++ (4.7.0) and icpc (13.0.1) compile fine (with option -std=c++11) and produce the expected output
ctor: P=0x7fffffffd480
test: P=0x7fffffffd480
2 if I declare
A&A::operator=(A&&) = delete;
A&A::operator=(const A&) = delete;
(which seems sensible in view of the constant data member which must be initialiser-list initialised), but don't provide any further ctor, compilation fails with g++ but is okay wich icpc. If in addition I define either (or both) of
A::A(A&&) = default;
A::A(const A&) = default;
both compilers are happy. However, g++ is not happy with the combination
A::A(A&&) = delete;
A::A(const A&) = default;
while icpc is happy.
3 If I play the same game as in 2, except that A::A(A&&) = default; is replaced by
A::A(A&&a) : P(a.P) { std::cerr<<" move ctor: P="<<P<<'\n'; } // never called?
(and equivalent for A::A(const A&)), the results are completely identical, in particular no output is generated from these explicit move and copy ctors.
So which operator is used for = in main()? (and why is no output produced in the last test?)
And why is this operation allowed here at all, given that A<> has a constant data member (the results are identical if I replace the member const*T const P; with const T&R)?
Finally, in case of the different behaviours of g++ and icpc, which, if any, is correct?
A<B> a = b.get_a();
is not an assignment, but an initialization of a from an rvalue. This syntax should fail under C++0x if
the move constructor is deleted,
the move constructor is declared explicit,
the copy constructor is deleted and no move constructor is defined,
no move constructor is defined and, at the same time the move-assignment is defined or deleted.
Declaration or deletion of the copy assignment operator should not have any influence.
Correction: Different to the copy constructor (which is synthesized even if a user-defined copy assignment operator is provided), the compiler does not synthesize a move constructor if a user-defined move assignment is defined. Hence, the above list should be amended by 4 (which I have done now).
Hence, in my opinion,
in [1] in the question, both compilers behave correctly,
in [2]/1, gcc behaves correctly (move assignment prevents generation of move constructor), icpc is wrong,
in [2]/2, both compilers are correct,
in [2]/3, gcc is correct, since the move constructor is explicitly deleted; icpc is wrong,
[3] is a mystery to me. Are you sure you're correct?
What are all the member-functions created by compiler for a class? Does that happen all the time? like destructor.
My concern is whether it is created for all the classes, and why is default constructor needed?
C++98/03
If they are needed,
the compiler will generate a default constructor for you unless you declare any constructor of your own.
the compiler will generate a copy constructor for you unless you declare your own.
the compiler will generate a copy assignment operator for you unless you declare your own.
the compiler will generate a destructor for you unless you declare your own.
As Péter said in a helpful comment, all those are only generated by the compiler when they are needed. (The difference is that, when the compiler cannot create them, that's Ok as long as they aren't used.)
C++11
C++11 adds the following rules, which are also true for C++14 (credits to towi, see this comment):
The compiler generates the move constructor if
there is no user-declared copy constructor, and
there is no user-declared copy assignment operator, and
there is no user-declared move assignment operator and
there is no user-declared destructor,
it is not marked deleted,
and all members and bases are moveable.
Similarly for move assignment operator, it is generated if
there is no user-declared copy constructor, and
there is no user-declared copy assignment operator, and
there is no user-declared move constructor and
there is no user-declared destructor,
it is not marked deleted,
and all members and bases are moveable.
Note that these rules are a bit more elaborate than the C++03 rules and make more sense in practice.
For an easier understanding of what is what in the above:
class Thing {
public:
Thing(); // default constructor
Thing(const Thing&); // copy c'tor
Thing& operator=(const Thing&); // copy-assign
~Thing(); // d'tor
// C++11:
Thing(Thing&&); // move c'tor
Thing& operator=(Thing&&); // move-assign
};
Further reading: if you are a C++-beginner consider a design that does not require you to implement any of five a.k.a The Rule Of Zero originally from an article written by Martinho Fernandes.
Do you mean 'defined' by 'created'?
$12.1 - "The default constructor (12.1), copy constructor and copy assignment operator (12.8), and destructor (12.4) are special member functions.
If 'created' means 'defined' then, here are the important parts from the C++ Standard.
-An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object of its class type (1.8).
-If a class has no user-declared destructor, a destructor is declared implicitly. An implicitly-declared destructor is implicitly defined when it is used to destroy an object of its class type.
-If the class definition does not explicitly declare a copy constructor, one is declared implicitly. An implicitly-declared copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type).
-If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. An implicitly-declared copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type.
C++17 N4659 standard draft
https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1 "Declarations and definitions" has a note which likely summarizes all of them:
3
[ Note: In some circumstances, C ++ implementations implicitly define the default constructor (15.1), copy
constructor (15.8), move constructor (15.8), copy assignment operator (15.8), move assignment operator (15.8),
or destructor (15.4) member functions. — end note ] [ Example: Given
#include <string>
struct C {
std::string s; // std::string is the standard library class (Clause 24)
};
int main() {
C a;
C b = a;
b = a;
}
the implementation will implicitly define functions to make the definition of C equivalent to
struct C {
std::string s;
C() : s() { }
C(const C& x): s(x.s) { }
C(C&& x): s(static_cast<std::string&&>(x.s)) { }
// : s(std::move(x.s)) { }
C& operator=(const C& x) { s = x.s; return *this; }
C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
// { s = std::move(x.s); return *this; }
~ C() { }
};
— end example ]
The conditions under which those are declared are explained at: Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?
A cool way to ensure that something has a default is to try and make it use = default as explained at: What does "default" mean after a class' function declaration?
The example below does that, and also exercises all the implicitly defined functions.
#include <cassert>
#include <string>
struct Default {
int i;
Default() = default;
Default(const Default&) = default;
Default& operator=(Default&) = default;
Default& operator=(const Default&) = default;
Default(Default&&) = default;
Default& operator=(Default&&) = default;
~Default() = default;
};
struct Instrument {
int i;
static std::string last_call;
Instrument() { last_call = "ctor"; }
Instrument(const Instrument&) { last_call = "copy ctor"; }
Instrument& operator=(Instrument&) { last_call = "copy assign"; return *this; }
Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
Instrument(Instrument&&) { last_call = "move ctor"; }
Instrument& operator=(Instrument&&) { last_call = "move assign"; return *this; }
~Instrument() { last_call = "dtor"; }
};
std::string Instrument::last_call;
int main() {
// See what the default constructors are doing.
{
// Default constructor.
Default ctor;
// i is uninitialized.
// std::cout << ctor.i << std::endl;
ctor.i = 1;
// Copy constructor.
Default copy_ctor(ctor);
assert(copy_ctor.i = 1);
// Copy assignment.
Default copy_assign;
copy_assign = ctor;
assert(copy_assign.i = 1);
// Copy assignment const.
const Default const_ctor(ctor);
Default copy_assign_const;
copy_assign_const = const_ctor;
assert(copy_assign_const.i == 1);
// Move constructor.
Default move_ctor(std::move(ctor));
assert(move_ctor.i == 1);
// Move assignment.
Default move_assign;
move_assign = std::move(ctor);
assert(move_assign.i == 1);
}
// Check that the constructors are called by these calls.
{
// Default constructor.
Instrument ctor;
assert(Instrument::last_call == "ctor");
// Copy constructor.
Instrument copy_ctor(ctor);
assert(Instrument::last_call == "copy ctor");
// Copy assignment.
copy_ctor = ctor;
assert(Instrument::last_call == "copy assign");
// Copy assignment const.
const Instrument const_ctor(ctor);
Instrument copy_assign_const;
copy_assign_const = const_ctor;
assert(Instrument::last_call == "copy assign const");
// Move constructor.
Instrument move_ctor(std::move(ctor));
assert(Instrument::last_call == "move ctor");
// Move assignment.
Instrument move_assign;
move_assign = std::move(ctor);
assert(Instrument::last_call == "move assign");
// Destructor.
{
Instrument dtor;
}
assert(Instrument::last_call == "dtor");
}
}
GitHub upstream.
Tested with GCC 7.3.0:
g++ -std=c++11 implicitly_defined.cpp
By default, if not implemented by the user, the compiler add some member functions to the class. Those are called the big four :
default constructor
copy constructor
copy operator (assignment)
destructor
Depending on the types of the members and which member function listed you provide yourself, those will not all be generated.
Other answers have told you what's created, and that the compiler may only generate them if used.
My concern is whether it is created for all the classes...
Why concerned? Thinking it's creating unwanted code in the executable? Unlikely, but you can check easily enough with your environment.
Or perhaps your concern was that it might not create a constructor when you want one? Nothing to worry about... they're always created if needed and not provided by the user.
...and why is default constructor needed?
Because classes may have objects inside them with their own destructors that need to be systematically invoked. For example, given...
struct X
{
std::string a;
std::string b;
};
...the default destructor makes sure the destructors for a and b run.