The section N3797::12.8/11 [class.copy] says:
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 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
The first case about the ambiguity of corresponding copy/move constructor is quite clear. We can write the following:
#include <iostream>
using namespace std;
struct A
{
A(){ }
A(volatile A&){ }
A(const A&, int a = 6){ }
};
struct U
{
U(){ };
A a;
};
U u;
U t = u;
int main(){ }
to reflect that. But what about or a function that is deleted or inaccessible from the defaulted constructor? What's that got with a function inaccessible from the default constructor? Could you provide an example reflecting that?
Simply put:
struct M { M(M const&) =delete; };
struct X { X(X const&) =default; M m; }; // X(X const&) is actually deleted!
Implicitly-declared functions are also considered "defaulted" ([dcl.fct.def.default] / 5); a more familiar pre-C++11 example might be something like:
struct M { protected: M(M const&); };
struct X { M m; }; // X's implicit copy constructor is deleted!
Note that if you explicitly default the function after it has been declared, the program is ill-formed if the function would be implicitly deleted ([dcl.fct.def.default] / 5):
struct M { M(M const&) =delete; };
struct X { X(X const&); M m; };
X::X(X const&) =default; // Not allowed.
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
The wording is perhaps slightly contrived, for conciseness most certainly. The idea, as emphasised in the above, is that the function in question is the M copy constructor being overloaded in way which renders it inaccessible. So having a member of class M whose copy constructor is made protected for instance would delete the copy constructor of X. Likewise, simply deleting the copy constructor of M would have the same result.
Related
Given base class A and derived class B, A has deleted move constructor:
class A {
public:
A() {}
A(const A&) = default;
A(A&&) = delete;
};
class B : public A
{
};
In such case, the following function does not compile because of deleted move constructor:
A f() {
A a;
return a;
}
but the similar function for B does not report any error:
B g() {
B b;
return b;
}
Does it mean that move constructor in B is not deleted? I want to know what is the rule in the standard.
The move constructor in B is deleted, but does not participate in overload resolution. According to cppreference:
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 or a non-static data member with a deleted or inaccessible destructor;
T is a union-like class and has a variant member with non-trivial move constructor.
A defaulted move constructor that is deleted is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue).
The second bullet point applies: B has a direct base class, A, with a deleted move constructor. So B's implicitly-declared move constructor is defined as deleted.
However, when the return statement is evaluating which constructor of B to use, the deleted move constructor is not considered but the valid copy constructor is.
The move constructor of B is deleted, as already answered by #Nathan Pierson.
The reason that you can return the local b from g is, as explained there, that the implicitly deleted move constructor of B is not participating in the overload resolution, thus the compiler selects the default copy constructor of B.
To prove the above take a look at the following code, adding a moveable only member into B:
class B : public A {
std::unique_ptr<int> ptr;
public:
B() {}
// needs this to compile:
// B(B&& b): A(b), ptr(std::move(b.ptr)) {}
};
B g() {
B b;
// now this would fail
// B doesn't have a default move ctor
// (it is implicitly deleted because of A)
// and the default copy ctor is not valid
return b;
}
Hisenter link description here
enter link description here
What is exactly the implicit move constructor doing? For example how would the implicit move constructor look like for the following class (could you provide some example implementation of this implicit constructor):
struct A
{
A() = default;
A(A && other) = default;
int a;
};
struct B : public A
{
int b;
int * c;
};
Would the implementation look like this:
B(B && other) : A(std::move(other)), b(std::move(other.b)), c(std::move(other.c)) {}
From cppreference.com:
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. If this satisfies the requirements of a constexpr
constructor, the generated move constructor is constexpr.
The base class constructors runs before the derived one.
As expected the following does not compile:
class A {
public:
A() = default;
//A(A&&) = default;
A(const A&) = delete;
int x;
};
int main()
{
auto a4 = A{}; // not ok, copy constructor is deleted
auto a5 = A(); // not ok, copy constructor is deleted
return 0;
}
But if a move constructor is added, even with the copy constructor explicitly deleted, then the following does compile:
class A {
public:
A() = default;
A(A&&) = default;
A(const A&) = delete;
int x;
};
int main()
{
auto a4 = A{}; // now ok, even though copy constructor is deleted
auto a5 = A(); // now ok, even though copy constructor is deleted
return 0;
}
Why isn't the deleted copy constructor considered?
Why isn't the deleted copy constructor considered?
It is considered. It's just not used, so the fact that it's deleted doesn't matter. That rule, from [dcl.fct.def.delete] is:
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
[ Note: [...] If a function
is overloaded, it is referenced only if the function is selected by overload resolution. [...] —end note ]
Overload resolution on auto a = A{}; (braces vs parens is equivalent in this case) finds two constructor candidates:
A(const A&);
A(A&& );
One of the rules for choosing which candidate is the "best viable" candidate is, from [over.match.best]:
Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if [...], and then
— [...]
— the context is an initialization by conversion function for direct reference binding (13.3.1.6) of a reference
to function type, the return type of F1 is the same kind of reference (i.e. lvalue or rvalue) as the
reference being initialized, and the return type of F2 is not
The move constructor is the same kind of reference (rvalue) as the parameter, whereas the copy constructor is not. Hence it's preferred and selected as the best viable candidate. Since A(const A&) wasn't selected by overload resolution, we're not referring to that constructor, so the code is fine.
If we actually used the copy constructor on the other hand (e.g. A a(a5)), that would actually attempt to use the copy constructor, which would be ill-formed.
N4296::12.8/11 [class.copy] told us the following:
A defaulted copy/move constructor for a class X is defined as deleted
(8.4.3) if X has:
[...]
(11.2) — 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
[...]
So it's not clear why the program
struct X
{
};
struct Y
{
X&& x;
Y(Y const&)= default;
};
int main() { }
DEMO
works fine, but the following:
struct X
{
};
struct Y
{
X&& x;
Y(Y const&);
};
Y::Y(Y const&)= default; //error
int main() { }
DEMO
[dcl.fct.def.default]/p5, emphasis mine:
A function is user-provided if it is user-declared and not
explicitly defaulted or deleted on its first declaration. A
user-provided explicitly-defaulted function (i.e., explicitly
defaulted after its first declaration) is defined at the point where
it is explicitly defaulted; if such a function is implicitly defined
as deleted, the program is ill-formed.
This makes sense as any use of a deleted function should render a program ill-formed, but with a user-provided explicitly-defaulted function, it may be impossible to diagnose this at the call site:
// y.h
struct X
{
};
struct Y
{
X&& x;
Y(Y const&);
};
// y.cpp
#include "y.h"
Y::Y(Y const&)= default; //defined as deleted
// main.cpp
#include "y.h"
int main() {
Y y = Y();
}
When compiling main.cpp, the compiler has no idea about Y's copy constructor other than that it exists; it doesn't know that it's defaulted, and can't possibly diagnose that it is actually deleted. The only place such a error can be diagnosed is where the function is explicitly defaulted.
Is this
struct Example {
string a, b;
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
Example& operator=(Example&& mE) { a = move(mE.a); b = move(mE.b); return *this; }
}
equivalent to this
struct Example {
string a, b;
Example(Example&& mE) = default;
Example& operator=(Example&& mE) = default;
}
?
Yes both are the same.
But
struct Example {
string a, b;
Example(Example&& mE) = default;
Example& operator=(Example&& mE) = default;
}
This version will permits you to skip the body definition.
However, you have to follow some rules when you declare explicitly-defaulted-functions :
8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]
A function definition of the form:
attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;
is called an explicitly-defaulted definition. A function that is explicitly defaulted shall
be a special member function,
have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared,
not have default arguments.
Is a =default move constructor equivalent to a member-wise move constructor?
Yes. Update: Well, not always. Look at this example:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) = default;
nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "copy" << std::endl; }
movable( movable &&) { std::cerr << "move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c)); // prints copy
}
It prints:
copy
http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb
You declared defaulted move constructor, but copying happens instead of moving. Why? Because if a class has even a single non-movable member then the explicitly defaulted move constructor is implicitly deleted (such a pun). So when you run has_nonmovable d = std::move(c), the copy constructor is actually called, because the move constructor of has_nonmovable is deleted (implicitly), it just doesn't exists (even though you explicitly declared the move constructor by expression has_nonmovable(has_nonmovable &&) = default).
But if the move constructor of non_movable was not declared at all, the move constructor would be used for movable (and for every member that has the move constructor) and the copy constructor would be used for nonmovable (and for every member that does not define the move constructor). See the example:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; }
//nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "movable::copy" << std::endl; }
movable( movable &&) { std::cerr << "movable::move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c));
}
It prints:
movable::move
nonmovable::copy
http://coliru.stacked-crooked.com/a/420cc6c80ddac407
Update: But if you comment out the line has_nonmovable(has_nonmovable &&) = default;, then copy will be used for both members: http://coliru.stacked-crooked.com/a/171fd0ce335327cd - prints:
movable::copy
nonmovable::copy
So probably putting =default everywhere still makes sense. It doesn't mean that your move expressions will always move, but it makes chances of this higher.
One more update: But if comment out the line has_nonmovable(const has_nonmovable &) = default; either, then the result will be:
movable::move
nonmovable::copy
So if you want to know what happens in your program, just do everything by yourself :sigh:
Yes, a defaulted move constructor will perform a member-wise move of its base and members, so:
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
is equivalent to:
Example(Example&& mE) = default;
we can see this by going to the draft C++11 standard section 12.8 Copying and moving class objects paragraph 13 which says (emphasis mine going forward):
A copy/move constructor that is defaulted and not defined as deleted
is implicitly defined if it is odrused (3.2) or when it is explicitly
defaulted after its first declaration. [ Note: The copy/move
constructor is implicitly defined even if the implementation elided
its odr-use (3.2, 12.2). —end note ][...]
and paragraph 15 which says:
The implicitly-defined copy/move constructor for a non-union class X
performs a memberwise copy/move of its bases and members. [ Note:
brace-or-equal-initializers of non-static data members are ignored.
See also the example in 12.6.2. —end note ] The order of
initialization is the same as the order of initialization of bases and
members in a user-defined constructor (see 12.6.2). Let x be either
the parameter of the constructor or, for the move constructor, an
xvalue referring to the parameter. Each base or non-static data member
is copied/moved in the manner appropriate to its type:
if the member is an array, each element is direct-initialized with the corresponding subobject of x;
if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
otherwise, the base or member is direct-initialized with the corresponding base or member of x.
Virtual base class subobjects shall be initialized only once by the
implicitly-defined copy/move constructor (see 12.6.2).
apart very pathological cases ... YES.
To be more precise, you have also to considered eventual bases Example may have, with exact same rules. First the bases -in declaration order- then the members, always in declaration order.