Why?! Why C++ requires the class to be movable even if it's not used!
For example:
#include <iostream>
using namespace std;
struct A {
const int idx;
// It could not be compileld if I comment out the next line and uncomment
// the line after the next but the moving constructor is NOT called anyway!
A(A&& a) : idx(a.idx) { cout<<"Moving constructor with idx="<<idx<<endl; }
// A(A&& a) = delete;
A(const int i) : idx(i) { cout<<"Constructor with idx="<<i<<endl; }
~A() { cout<<"Destructor with idx="<<idx<<endl; }
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
The output is (the move constructor is not called!):
Constructor with idx=0
Constructor with idx=1
Destructor with idx=1
Destructor with idx=0
The code can not be compiled if moving constructor is deleted ('use of deleted function ‘A::A(A&&)’'. But if the constructor is not deleted it is not used!
What a stupid restriction?
Note:
Why do I need it for? The practical meaning appears when I am trying to initialize an array of objects contains unique_ptr field.
For example:
// The array of this class can not be initialized!
class B {
unique_ptr<int> ref;
public:
B(int* ptr) : ref(ptr)
{ }
}
// The next class can not be even compiled!
class C {
B arrayOfB[2] = { NULL, NULL };
}
And it gets even worse if you are trying to use a vector of unique_ptr's.
Okay. Thanks a lot to everybody. There is big confusion with all those copying/moving constructors and array initialization. Actually the question was about the situation when the compiler requires a copying consrtuctor, may use a moving construcor and uses none of them.
So I'm going to create a new question a little bit later when I get a normal keyboard. I'll provide the link here.
P.S. I've created more specific and clear question - welcome to discuss it!
A a[2] = { 0, 1 };
This is aggregate initialization. §8.5.1 [dcl.init.aggr]/p2 of the standard provides that
When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause.
§8.5 [dcl.init]/p16, in turn, describes the semantics of copy initialization of class type objects:
- [...]
- If the destination type is a (possibly cv-qualified) class type:
- If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source
type is the same class as, or a derived class of, the class of the
destination, constructors are considered. The applicable constructors
are enumerated (13.3.1.3), and the best one is chosen through overload
resolution (13.3). The constructor so selected is called to initialize
the object, with the initializer expression or expression-list as its
argument(s). If no constructor applies, or the overload resolution is
ambiguous, the initialization is ill-formed.
- Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source
type to the destination type or (when a conversion function is used)
to a derived class thereof are enumerated as described in 13.3.1.4,
and the best one is chosen through overload resolution (13.3). If the
conversion cannot be done or is ambiguous, the initialization is
ill-formed. The function selected is called with the initializer
expression as its argument; if the function is a constructor, the call
initializes a temporary of the cv-unqualified version of the
destination type. The temporary is a prvalue. The result of the call
(which is the temporary for the constructor case) is then used to
direct-initialize, according to the rules above, the object that is
the destination of the copy-initialization. In certain cases, an
implementation is permitted to eliminate the copying inherent in this
direct-initialization by constructing the intermediate result directly
into the object being initialized; see 12.2, 12.8.
Since 0
and 1
are int
s, not A
s, the copy initialization here falls under the second clause. The compiler find a user-defined conversion from int
to A
in your A::A(int)
constructor, calls it to construct a temporary of type A
, then performs direct initialization using that temporary. This, in turn, means the compiler is required to perform overload resolution for a constructor taking a temporary of type A
, which selects your deleted move constructor, which renders the program ill-formed.
A a[2] = { 0, 1 };
Conceptually, this creates two temporary A
objects, A(0)
and A(1)
, and moves or copies them to initialise the array a
; so a move or copy constructor is required.
As an optimisation, the move or copy is allowed to be elided, which is why your program doesn't appear to use the move constructor. But there must still be a suitable constructor, even if its use is elided.