Does std::is_move_constructible<T>::value == true imply that T has a usable move constructor?
If so, what is the default behaviour of it?
Consider the following case:
struct foo {
int* ptr;
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
f.ptr = (int*)12;
foo f2(std::move(f));
std::cout << f.ptr << ' ' << f2.ptr << '\n';
}
return 0;
}
and the output is:
1
0000000C 0000000C
I thought that f.ptr should be nullptr.
So in this case,
Is f2 move constructed ?
If so, shouldn't the rvalue be invalidated?
How can I know if instances of a class can be properly move-constructed (invalidate the old one)?
(I'm using VS11.)
Update
The default behaviour of move constructor is same as a copy constructor, is it correct?
If it's true,
We always expect a move ctor to steal the resources of the moved-from object, while the default one does not behave as expected, so what's the point of having a default move ctor?
How can I know if a class has a custom move constructor (which can be guaranteed to behave properly)?
It seems that foo f2(std::move(f)); calls the copy ctor when I declared one, see:
struct foo {
int* ptr;
foo() {}
foo(const foo& other) {
std::cout << "copy constructed\n";
}
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
foo f2(std::move(f));
}
system("pause");
return 0;
}
Now the output is:
1
copy constructed
If foo has a move constructor, then wouldn't foo f2(std::move(f)) call it?
So now my questions is:
How to know if a class has a move ctor, and if it has one, how can I explicitly call it?
What I'm trying to do is…
template<typename T, bool has_move_ctor>
struct MoveAux;
template<typename T>
struct MoveAux<T, true> {
static void doMove(T* dest, T* src) {
new(dest) T(std::move(*src)); //move ctor
}
};
template<typename T>
struct MoveAux<T, false> {
static void doMove(T* dest, T* src) {
new(dest) T(*src); //copy ctor
src->~T();
}
};
template<typename T>
inline doMove(T* dest, T* src) {
MoveAux<T,/*a trait*/>::doMove(dest, src);
}
So I thought std::is_move_constructible<T>::value can be passed to the template, while now I see that this trait only cares if T t(T()) is a valid expression, it may call T::T(const T&).
Now assume that T is a custom class, then I want the above templates to behave like:
If I don't declare a move ctor, I want that template method calls the MoveAux<T,false>::doMove.
If I declared one, I need it calls to MoveAux<T,true>::doMove.
Is it possible to make this work?
does std::is_move_constructible<T>::value == true implies that T has a usable move constructor?
Either a move constructor or a copy constructor. Remember that the operation of copy construction satisfies all the requirements that are placed upon the operation move construction, and some more.
In Standard terms, a MoveConstructible object is one for which the evaluation of the expression:
T u = rv;
makes u equivalent to the value of rv before the construction; the state of rv after being moved-from is unspecified. But since it is unspecified, this means the state could even be identical to the one rv had before being moved from: In other words, u could be a copy of rv.
In fact, the Standard defines the CopyConstructible concept to be a refinement of the MoveConstructible concept (so everything which is CopyConstructible is also MoveConstructible, but not vice versa).
if so, what is the default behaviour of it?
The behavior of an implicitly generated move constructor is to perform a member-wise move of the data members of the type for which it is generated.
Per Parahgraph 12.8/15 of the C++11 Standard:
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 ]
Moreover:
1 - is f2 move constructed ?
Yes.
2 - if so, shouldn't the rvalue be invalidated?
Moving a pointer is the same as copying it. So no invalidation is going on, neither should it be going on. If you want a move constructor that leaves the moved-from object in a particular state (i.e. sets a pointer data member to nullptr), you have to write your own - or delegate this responsibility to some smart pointer class such as std::unique_ptr.
Notice, that the word "invalidated" is not quite correct here. Move constructors (as well as move assignment operators) are meant to leave the moved-from object in a valid (yet unspecified) state.
In other words, the class invariant needs to be respected - and it should be possible to invoke on a moved-from objects operations that do not have any precondition on its state (usually, destruction and assignment).
does std::is_move_constructible::value == true implies that T has a usable move constructor?
No. It states that you can take an rvalue expression of the object type and construct an object from it. Whether this uses the move constructor or the copy constructor is not relevant to this trait.
is f2 move constructed ?
Yes.
if so, shouldn't the rvalue be invalidated?
No. That's not how movement works.
how can I know if instances of a class can be properly move-constructed(invalidate the old one)?
That is not any definition of "properly move-constructed" that exists. If you want to "invalidate the old one", then you will have to do that yourself.
Move construction generally guarantees nothing about the state of the old object. It will be in a valid but undefined state. Such state very much can be "the same as it was before". Move construction for a pointer is the same as copying the pointer.
If you want to "invalidate" after a move, then you need to write your own move constructor that explicitly does that.
(I'm using VS11)
Then you have no compiler-generated move constructors at all. Not that it would matter, since the move and copy constructors for pointers both do the same thing.
the default behaviour of move constructor is same as a copy
constructor, is it correct? if it's true
No. It's wrong. It's true only for primitives. It's similar to that of copy constructor.
The default generated copy constructor calls the copy constructor of all its members in the declared order
But The default generated move constructor calls the move constructor of all its members in the declared order
Now the next question is, what is the copy/move constructor of the primitives ints floats pointers do?
Answer: They just copy the values (both copy and move constructor)
Note that Visual Studio 2012 / VC++11 does not support compiler generated move constructors; in fact, consider this quote from "C++11 Features in Visual C++ 11" blog post (emphasis mine):
Rvalue references v3.0 adds new rules to automatically generate move
constructors and move assignment operators under certain conditions.
This will not be implemented in VC11, which will continue to follow
VC10's behavior of never automatically generating move
constructors/move assignment operators.
With raw pointers, you have to define move constructors by yourself, manually clearing the old "moved-from" pointer:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(other.m_ptr) // copy pointer value
{
// Clear out old "moved-from" pointer, to avoid dangling references
other.m_ptr = nullptr;
}
private:
int* m_ptr;
};
Instead, if you use a smart pointer like std::unique_ptr, move constructor is properly defined, and you can just call std::move:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(std::move(other.m_ptr)) // move from other,
// old pointer automatically cleared
{
}
private:
std::unique_ptr<int> m_ptr;
};
With automatically generated move constructors, you don't have to define a custom move constructor explicitly, if member-wise move is OK for you.
n3376 12.8/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members.
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.
if foo has a move constructor, then wouldn't foo f2(std::move(f)) calls it?
You do not get the default move constructor when you supply your copy constructor. Add following line to get it ( and notice the change ).
foo(foo&& ifm)=default;
Related
I am struggling to understand implicit move operations when a class has a member whose move operations were not defined:
int main() {
struct A // no move: move = copy
{
A() = default;
A(const A&) {
cout << "A'copy-ctor\n";
};
A& operator=(const A&) {
cout << "A'copy-assign\n";
return *this;
}
};
struct B
{
B() = default;
A a; // does this make B non-moveable?
unique_ptr<int> upi;
// B(B&&) noexcept = default;
// B& operator=(B&&)noexcept = default;
};
A a;
A a2 = std::move(a); // ok use copy ctor instead of move one
a2 = std::move(a); // ok use copy assignment instead of move one
B b;
B b2 = std::move(b); // why this works?
b = std::move(b2); // and this works?
// b = b2; // error: copy deleted because of non-copyable member upi
cout << "\nDone!\n";
}
So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.
Until here it is OK if i am correct. But B has a non-copy-able object upi which is a unique_ptr thus the copy operations are defined as deleted functions so we cannot copy objects of this class. But this class has a non-move-able object a thus i think that this class (B) is neither copy-able nor move-able. But why the initialization of b2 and the assignment of b works fine? What happens exactly?
B b2 = std::move(b); // ok?!
Why the line above invokes the copy constructor of class A and does it invoke move constructor of B?
Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!
Can anyone help me what happens exactly? I have googled and read in cppreference and many websites before posting the question here.
The output:
A'copy-ctor
A'copy-assign
A'copy-ctor
A'copy-assign
Done!
Keep in mind what it means to "move" data in C++ (assuming we follow the usual conventions). If you move object x to object y, then y receives all the data that was in x and x is... well, we don't care what x is as long as it is still valid for destruction. Often we think of x as losing all of its data, but that is not required. All that is required is that x is valid. If x ends up with the same data as y, we don't care.
Copying x to y causes y to receive all the data that was in x, and x is left in a valid state (assuming the copy operation follows conventions and is not buggy). Thus, copying counts as moving. The reason for defining move operations in addition to copy operations is not to permit something new, but to permit greater efficiency in some cases. Anything that can be copied can be moved unless you take steps to prevent moves.
So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.
What I see is that A is a moveable class (despite the lack of move constructor and move assignment), because of the definition of its copy control operations. Any attempt to move an object of this class will fall back on the corresponding copy operation. If you want a class to be copyable but not movable, you need to delete the move operations, while retaining the copy ones. (Try it. Add A(A&&) = delete; to your definition of A.)
The B class has one member that can be moved or copied, and one member that can be moved but not copied. So B itself can be moved but not copied. When B is moved, the unique_ptr member will be moved as you expect, and the A member will be copied (the fallback for moving objects of type A).
Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!
Read the error message more closely. When I replicated this result, the "use of deleted function" error was followed by a note providing more details: the move constructor was deleted because "its exception-specification does not match the implicit exception-specification". Removing the noexcept keywords allowed the code to compile (using gcc 9.2 and 6.1).
Alternatively, you could add noexcept to the copy constructor and copy assignment of A (keeping noexcept on the move operations of B). This is one way to demonstrate that the default move operations of B use the copy operations of A.
Here is a summary of #JaMiT's excellent answer:
Class A is moveable via it's copy-constructor and copy-assignment operator, even though class A is not MoveConstructible and is not MoveAssignable. See the notes on cppreference.com's pages for MoveConstructible and MoveAssignable.
And thus class B is also moveable.
The language allows you to prevent moveability for class A by explicitly =delete'ing the move-constructor and move-assignment, even though class A is still copyable.
Is there a practical reason to have a copyable but not-moveable class? Someone asked just this question several years ago here. The answers and comments struggled to find any practical reason to want a copyable but not-moveable class.
std::move does not force object to be copied. It just returns &&-reference (which allows compiler to use move ctor/assign operator).
In cases 1,2 object is copied.
In 3,4 cases (i think) object is moved. But A is still copied because it cannot be moved.
This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 4 years ago.
Today I encountered something I don't really understand about copy constructor.
Consider the next code:
#include <iostream>
using namespace std;
class some_class {
public:
some_class() {
}
some_class(const some_class&) {
cout << "copy!" << endl;
}
some_class call() {
cout << "is called" << endl;
return *this; // <-- should call the copy constructor
}
};
some_class create() {
return some_class();
}
static some_class origin;
static some_class copy = origin; // <-- should call the copy constructor
int main(void)
{
return 0;
}
then the copy constructor is called when assigning origin to copy, which makes sense. However, if I change the declaration of copy into
static some_class copy = some_class();
it isn't called. Even when I am using the create() function, it does not call the copy constructor.
However, when changing it to
static some_class copy = some_class().call();
it does call the copy constructor.
Some research explained that the compiler is allowed to optimize the copy-constructor out, which sound like a good thing. Until the copy-constructor is non-default, as then it might, or might not do something obvious, right?
So when is it allowed for the compiler to optimize out the copy-constructor?
Since C++17, this kind of copy elision is guaranteed, the compiler is not just allowed, but required to omit the copy (or move) construction, even if the copy (or move) constructor has the observable side-effects.
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, as the language rules ensure that no copy/move operation takes place, even conceptually:
In the initialization of a variable, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the
variable type:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:
T f() {
return T();
}
f(); // only one call to default constructor of T
Your code snippet matches these two cases exactly.
Before C++17, the compiler is not required, but allowed to omit the copy (or move) construction, even if the copy/move constructor has observable side-effects. Note that it's an optimization, even copy elision takes place the copy (or move) constructor still must be present and accessible.
On the other hand, call() doesn't match any conditions of copy elision; it's returning *this, which is neither a prvalue nor a object with automatic storage duration, copy construction is required and can't be omitted.
Why am I allowed to use std::move on a class which contains fields of a type with deleted move semantics (case 1), but am not allowed to use it on an instance of such a class (case 2)?
I understand case 2. I have explicitly deleted the move constructor, so I get an error if I try to move it. But I would expect that this would also be the case in case 1, where such class is also being moved.
class TestNonMovable {
std::string ss;
public:
TestNonMovable(){}
TestNonMovable(const TestNonMovable&) {}
TestNonMovable(TestNonMovable&&) = delete;
};
class SomeClass {
TestNonMovable tnm;
};
int main() {
// case1: This compiles, my understanding is that for SomeClass::tnm compiler will use copy constrctor
SomeClass sc1;
SomeClass sc2 = std::move(sc1);
// case2: This does not compile, move constructor is explicitly deleted, compiler will not try using copy constructor
TestNonMovable tnm;
TestNonMovable tnm2 = std::move(tnm); //error: use of deleted function 'TestNonMovable::TestNonMovable(TestNonMovable&&)'
}
Note the difference between the two classes. For TestNonMovable (case 2), you explicitly declare the move constructor as delete. With TestNonMovable tnm2 = std::move(tnm); the deleted move constructor is selected in overload resolution and then cause the error.
For SomeClass (case 1), you don't explicitly declare the move and copy constructor. The copy constructor will be implicitly declared and defined, but the move constructor will be implicitly declared and defined as deleted because it has a non-movable data member. Note the deleted implicitly declared move constructor won't participate in overload resolution. With SomeClass sc2 = std::move(sc1);, the copy constructor is selected and then the code works fine. The rvalue reference returned from std::move could be bound to lvalue-reference to const (i.e. const SomeClass&) too.
The deleted implicitly-declared move constructor is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue). (since C++14)
BTW: std::move itself is allowed in both cases. It doesn't perform the move operation, it just converts the argument to rvalue. The move operation happens at construction in both cases.
What is the rationale behind the different treatment of implicitly and explicitly deleted move constructors in the C++11 standard, with respect to the implicit generation of move constructors of containing/inheriting classes?
Do C++14/C++17 change anything? (Except DR1402 in C++14)
Note: I understand what is happening, I understand that it is according to the C++11 standard's rules, I'm interested in the rationale for these rules that imply this behavior (please make sure not to simply restate that it is the way it is because the standard says so).
Assume a class ExplicitDelete with an explicitly deleted move ctor and an explicitly defaulted copy ctor. This class isn't move constructible even though a compatible copy ctor is available, because overload resolution chooses the move constructor and fails at compile time due to its deletion.
Assume a class ImplicitDelete which either contains or inherits from ExplicitDelete and does nothing else. This class will have its move ctor implicitly declared as deleted due to C++11 move ctor rules. However, this class will still be move constructible via its copy ctor. (Does this last statement have to do with resolution of DR1402?)
Then a class Implicit containing/inheriting from ImplicitDelete will have a perfectly fine implicit move constructor generated, that calls ImplicitDelete's copy ctor.
So what is the rationale behind allowing Implicit to be able to move implicitly and ImplicitDelete not to be able to move implicitly?
In practice, if Implicit and ImplicitDelete have some heavy-duty movable members (think vector<string>), I see no reason that Implicit should be vastly superior to ImplicitDelete in move performance. ImplicitDelete could still copy ExplicitDelete from its implicit move ctor—just like Implicit does with ImplicitDelete.
To me, this behavior seems inconsistent. I'd find it more consistent if either of these two things happened:
The compiler treats both the implicitly and explicitly deleted move ctors the same:
ImplicitDelete becomes not move-constructible, just like ExplicitDelete
ImplicitDelete's deleted move ctor leads to a deleted implicit move ctor in Implicit (in the same way that ExplicitDelete does that to ImplicitDelete)
Implicit becomes not move-constructible
Compilation of the std::move line utterly fails in my code sample
Or, the compiler falls back to copy ctor also for ExplicitDelete:
ExplicitDelete's copy constructor is called in all moves, just like for ImplicitDelete
ImplicitDelete gets a proper implicit move ctor
(Implicit is unchanged in this scenario)
The output of the code sample indicates that the Explicit member is always moved.
Here's the fully working example:
#include <utility>
#include <iostream>
using namespace std;
struct Explicit {
// prints whether the containing class's move or copy constructor was called
// in practice this would be the expensive vector<string>
string owner;
Explicit(string owner) : owner(owner) {};
Explicit(const Explicit& o) { cout << o.owner << " is actually copying\n"; }
Explicit(Explicit&& o) noexcept { cout << o.owner << " is moving\n"; }
};
struct ExplicitDelete {
ExplicitDelete() = default;
ExplicitDelete(const ExplicitDelete&) = default;
ExplicitDelete(ExplicitDelete&&) noexcept = delete;
};
struct ImplicitDelete : ExplicitDelete {
Explicit exp{"ImplicitDelete"};
};
struct Implicit : ImplicitDelete {
Explicit exp{"Implicit"};
};
int main() {
ImplicitDelete id1;
ImplicitDelete id2(move(id1)); // expect copy call
Implicit i1;
Implicit i2(move(i1)); // expect 1x ImplicitDelete's copy and 1x Implicit's move
return 0;
}
So what is the rationale behind allowing Implicit to be able to move implicitly and ImplicitDelete not to be able to move implicitly?
The rationale would be this: the case you describe does not make sense.
See, all of this started because of ExplicitDelete. By your definition, this class has an explicitly deleted move constructor, but a defaulted copy constructor.
There are immobile types, with neither copy nor move. There are move-only types. And there are copyable types.
But a type which can be copied but has an explicitly deleted move constructor? I would say that such a class is a contradiction.
Here are the three facts, as I see it:
Explicitly deleting a move constructor is supposed to mean you can't move it.
Explicitly defaulting a copy constructor is supposed to mean you can copy it (for the purposes of this conversation, of course. I know you can still do things that make the explicit default deleted instead).
If a type can be copied, it can be moved. That's why the rule about implicitly deleted move constructors not participating in overload resolution exists. Therefore, movement is a proper subset of copying.
The behavior of C++ in this instance is inconsistent because your code is contradictory. You want your type to be copyable but not moveable; C++ does not allow that, so it behaves oddly.
Look at what happens when you remove the contradiction. If you explicitly delete the copy constructor in ExplicitDelete, everything makes sense again. ImplicitDelete's copy/move constructors are implicitly deleted, so it is immobile. And Implicit's copy/move constructors are implicitly deleted, so it too is immobile.
If you write contradictory code, C++ will not behave in an entirely legitimate fashion.
I've already tried to ask this question but I wasn't clear enough. So here is one more try. And I am very sorry for my English ;)
Let's see the code:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
It can not be compiled because unique_ptr has deleted copying constructor.
Orly?!
This class DOES HAVE an implied moving constructor because unique_ptr has one.
Let's do a test:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
// Let's add a moving constructor.
A(A&& a) : ref(std::move(a.ref)) {
cout<<"Moving constructor with";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
I've added a moving constructor and now the code can be compiled and executed.
Even if the moving constructor is not used.
The output:
Constructor with i=0
Constructor with i=1
Destructor withi=1
Destructor withi=0
Okay...Let's do one more test and remove the copying constructor (but leave the moving one).
I don't post the code, there only one line has been added:
A(const A& a) = delete;
You should trust me - it works. So the compiler doesn't require a copying constructor.
But it did! (a facepalm should be here)
So what's going on? I see it completely illogical! Or is there some sort of twisted logic I don't see?
Once more:
unique_ptr has a moving constructor and has a deleted copying constructor. Compiler requires copying constructor to be present. But in fact the compiler requires a moving constructor (even if it is not used) and doesn't require a copying (because it could be deleted). And as I see the moving constructor is (should be?) present impliedly.
What's wrong with that?
P.S. One more thing - if I delete the moving constructor the program could not be compiled. So the moving constructor is required, but not the copying one.Why does it require copy-constructor if it's prohibited to use it there?
P.P.S.
Big thanks to juanchopanza's answer! This can be solved by:
A(A&& a) = default;
And also big thanks to Matt McNabb.
As I see it now, the moving constructor is absent because unique_ptr doesn't have a copying one the class has a destructor (and the general rule is that default/copying/moving constructors and destructor could be generated by default only all together). Then the compiler doesn't stop at moving one (why?!) and falls back to copying one. At this point the compiler can't generate it and stops with an error (about the copy constructor) when nothing else can be done.
By the way it you add:
A(A&& a) = delete;
A(const A& a) = default;
It could NOT be compiled with error about 'A::A(A&& a)' deletion, There will be no fall back to copying constructor.
P.P.P.S The last question - why does it stop with error at the COPY constructor but not the MOVE constructor?!
GCC++ 4.7/4.8 says: "error: use of deleted function ‘A::A(const A&)’"
So it stops at copy constructor.
Why?! There should be 'A::A(A&&)'
Ok. Now it seems like a question about move/copy constrcutor choosing rule.
I've created the new more specific question here
This is called copy elision.
The rule in this situation is that a copy/move operation is specified, but the compiler is allowed to optionally elide it as an optimization, even if the copy/move constructor had side-effects.
When copy elision happens, typically the object is created directly in the memory space of the destination; instead of creating a new object and then copy/moving it over to the destination and deleting the first object.
The copy/move constructor still has to actually be present, otherwise we would end up with stupid situations where the code appears to compile, but then fails to compile later when the compiler decides not to do copy-elision. Or the code would work on some compilers and break on other compilers, or if you used different compiler switches.
In your first example you do not declare a copy nor a move constructor. This means that it gets an implicitly-defined copy-constructor.
However, there is a rule that if a class has a user-defined destructor then it does not get an implicitly-defined move constructor. Don't ask me why this rule exists, but it does (see [class.copy]#9 for reference).
Now, the exact wording of the standard is important here. In [class.copy]#13 it says:
A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odr-used (3.2)
[Note: The copy/move constructor is implicitly defined even if the implementation elided its odr-use (3.2, 12.2). —end note
The definition of odr-used is quite complicated, but the gist of it is that if you never attempt to copy the object then it will not try to generate the implicitly-defined copy constructor (and likewise for moving and move).
As T.C. explains on your previous thread though, the act of doing A a[2] = {0, 1}; does specify a copy/move, i.e. the value a[0] must be initialized either by copy or by move, from a temporary A(0). This temporary is able to undergo copy elision, but as I explain earlier, the right constructors must still exist so that the code would work if the compiler decides not to use copy elision in this case.
Since your class does not have a move constructor here, it cannot be moved. But the attempt to bind the temporary to a constructor of A still succeeds because there is a copy-constructor defined (albeit implicitly-defined). At that point, odr-use happens and it attempts to generate the copy-constructor and fails due to the unique_ptr.
In your second example, you provide a move-constructor but no copy-constructor. There is still an implicitly-declared copy-constructor which is not generated until it is odr-used, as before.
But the rules of overload resolution say that if a copy and a move are both possible, then the move constructor is used. So it does not odr-use the copy-constructor in this case and everything is fine.
In the third example, again the move-constructor wins overload resolution so it does not matter what how the copy-constructor is defined.
I think you are asking why this
A a[2] = { 0, 1 };
fails to compile, while you would expect it to compile because A may have a move constructor. But it doesn't.
The reason is that A has a member that is not copyable, so its own copy constructor is deleted, and this counts as a user declared copy constructor has a user-declared destructor.
This in turn means A has no implicitly declared move constructor. You have to enable move construction, which you can do by defaulting the constructor:
A(A&&) = default;
To check whether a class is move constructible, you can use is_move_constructible, from the type_traits header:
std::cout << std::boolalpha;
std::cout << std::is_move_constructible<A>::value << std::endl;
This outputs false in your case.
The twisted logic is that you are supposed to write programs at a higher abstraction level. If an object has a copy constructor it can be copied, otherwise it cannot. If you tell the compiler this object shall not be copied it will obey you and not cheat. Once you tell it that it can be copied the compiler will try to make the copy as fast as possible, usually by avoiding the copy constructor.
As for the move constructor: It is an optimization. It tends to be faster to move an object from one place to another than to make an exact copy and destroy the old one. This is what move constructors are for. If there is no move constructor the move can still be done with the old fashioned copy and destroy method.