Move constructor is required even if it is not used. Why? - c++

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 };
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.

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 ints, not As, 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.

Related

Return from function using copy-list-initialization, no copy/move constructor needed - Where's it stated in C++ 11 standard?

struct A {
A() {}
A(const A&) = delete;
};
A foo() {
return {}; // Calls A()
}
struct B {
B(const B&) = delete;
};
B bar() {
return {}; // Aggregate initialization.
}
foo and bar both compile fine in C++11, because they use copy-list-initialization. There is no copy elision needed.
Where is it mentioned in the C++ standard that in such cases copy/move constructors are not needed?
I can see in [stmt.return] A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list.
I cannot find the section that mentions that in such cases copy/move constructors are not needed.
This is a C++11 (and 14, and 17) issue where aggregate-initialization is allowed to bypass the copy-constructor check.
B is an aggregate class (in C++11/14/17)
You can verify that in C++17 with the std::is_aggregate type trait
You are using list-initialization
This performs aggregate initialization, which is treated slightly differently than a regular constructor
Per #NicolBolas' nice answer regarding [stmt.return], return {} is like performing copy-list-initialization (aggregate initialzation) of the returned object directly (as opposed to constructing and then returning)
If you had written return B() instead, the compiler would have rejected this code.
The bypass is fixed in C++20 (The compiler will reject your code) per P1008 since B is no longer an aggregate type (C++20 says that a class with any user-declared constructors is not an aggregate).
Where is it mentioned in the C++ standard that in such cases copy/move constructors are not needed?
It isn't. There is no need to mention such a thing because copy-list-initialization is not specified to do any copying or moving.
The behavior of return {...}; is defined as follows:
A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization
So this syntax invokes copy-list-initialization, with the braced-init-list being the initializer and the function's return value object being the object to be initialized. So it is functionally equivalent to T return_value_object = {...};.
[dcl.init.list]/3 explains the entire process of list-initialization, and nowhere does it say that the object to be initialized is copied or moved from some T (where T is the object type being initialized). No temporary object of type T is created or anything of the kind. The braced-init-list simply initializes the object.
So there is no need for a copy or move constructor on T unless the members of the braced-init-list would themselves require one (if you provided an object of type T as a member of the list, for example).
Note that this is not elision. Elision implies that a copy/move would have happened but was optimized out. List initialization doesn't have any copying or moving to optimize away to begin with.

C++ direct list initialization with deleted default constructor

I have the following class definition. I included a private x to make sure it is not an aggregate.
class A {
private:
int x;
public:
A() = delete;
A(std::initializer_list<int>) { printf("init\n"); }
};
Now if I initialize this object with A a = A{}, it will say A::A() is deleted. I guess it is trying to call A::A() but it is deleted. If I comment out that line, so A::A() is automatically generated. Then if I run this code, I could see that it is calling A::A(std::initializer_list<int>)!
And more confusing, if I define A() = default, the initialization calls A::A() again.
Can anyone point me to the right direction of understading this behavior? Thanks!
I'm using G++ 6.3.0 with flag -std=c++17.
The behaviour is correct.
First of all:
// Calling
A a = A{}; // equals A a = A();,
which was a convenience uniformization of the list-initialization idiom.
Refer to: https://stackoverflow.com/a/9020606/3754223
Case A:
class A {
private:
int x;
public:
**A() = delete;**
A(std::initializer_list<int>) { printf("init\n"); }
};
As said above, A a = A{} will be ... = A(), which is deleted.
Case B:
class A {
private:
int x;
public:
A(std::initializer_list<int>) { printf("init\n"); }
};
In this case, there's no default constructor provided, since you have your initializer-list constructor be defined. Consequently {} is converted to an empty initializer list implicitely!
Case C:
class A {
private:
int x;
public:
**A() = default;**
A(std::initializer_list<int>) { printf("init\n"); }
};
In this case the empty default constructor is available and A{} is converted back to A(), causing it to be called.
Please refer to the below pages and get a thorough read into it.
http://en.cppreference.com/w/cpp/utility/initializer_list
http://en.cppreference.com/w/cpp/language/value_initialization
Finally:
Using:
A a = {{}};
Would cause that ALWAYS the initializer-list constructor is called, as the outer-curly-brackets denoted the init-list, and the inner a zero-constructed element. -> Non-empty initializer-list... (See stackoverflow link above again!)
And btw, I cannot understand why the question is downvoted, considering that this is a really tricky part...
You have lots of cases listed, so let us go over them one by one.
A() = delete;
A(std::initializer_list<int>) { ... }
Writing A a = A{}; will indeed try to call the deleted default constructor, and your code fails to compile.
What you have above is list initialization, and because the braced-init-list is empty, value initialization will be performed. This selects the deleted default constructor, which makes your code ill-formed.
If I comment out that line, so A::A() is automatically generated
No, this is not the case. There is no implicitly declared default constructor due to the presence of the user provided constructor that takes an initializer_list. Now, A a = A{}; will call the initializer_list constructor, and in this case the initializer_list will be empty.
if I define A() = default, the initialization calls A::A() again
This behaves the same as the first case, except the default constructor is explicitly defaulted instead of deleted, so your code compiles.
Finally,
I included a private x to make sure it is not an aggregate
There's no need for this, defining a constructor makes A a non-aggregate.
List initialization follows a very specific ordering, as laid out in [dcl.init.list]/3. The two relevant bullet points and their relative ordering are highlighted:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate class and [...]
— Otherwise, if T is a character array and [...]
— Otherwise, if T is an aggregate, [...]
— Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T is a specialization of std::initializer_list<E>, [...]
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).
— Otherwise, if the initializer list has a single element of type E and [...]
— Otherwise, if T is a reference type, [...]
— Otherwise, if T is an enumeration with a fixed underlying type (7.2), [...]
— Otherwise, if the initializer list has no elements, the object is value-initialized.
— Otherwise, the program is ill-formed.
An empty initializer list for class types with default constructors is a special case that precedes normal constructor resolution. Your class isn't an aggregate (since it has a user-provided constructor), so A{} will attempt to value-initialize A if A has a default constructor. It doesn't matter if that default constructor is accessible, it only matters if it exists. If it exists and is inaccessible (your first case), that's ill-formed. If it exists and is accessible (your third case), then it's invoked. Only if your class does not have a default constructor will the constructors be enumerated, in which case the A(initializer_list<int> ) constructor will be invoked with an empty initializer list (your second case).

Confusion in copy initialization and direct initialization

Consider simple statement (Taken from Is there a difference in C++ between copy initialization and direct initialization?):
A c2 = A();
This statement value-initializes a temporary and then copies that
value into c2 (Read 5.2.3/2 and 8.5/14). This of course will require a
non-explicit copy constructor (Read 8.5/14 and 12.3.1/3 and
13.3.1.3/1)
[Mind the bold sentence in above para] -> My question is why?
Now consider this code :
class B {};
struct A
{
A(B const&) {}
A(A const&) = delete;
//A(A const&); //delete above statement and uncomment this statement,
//and everything works, even though there in no body of copy constructor Oo
};
A a2 = B(); //error since there is no copy constructor oO
Why copy-initialization requires presence of copy constructor even though it's not needed sometime as presented in above code
Please please one more thing :
While direct initialization has all constructors available to call,
and in addition can do any implicit conversion it needs to match up
argument types, copy initialization can just set up one implicit
conversion sequence.
[Mind the bolding in the following para]
Doesn't that means direct initialization have access to all constructors and can perform implicit conversion sequence , while copy initialization all can do is perform implicit conversion sequence? . What I mean to ask is , implicit conversion in direct initialization is different from implicit conversion sequence in copy initialization ?
The rules for evaluating
A a1 = B(); // (1) copy-initialization
A a2 = {B()}; // (2) copy-initialization
A a3{B()}; // (3) direct-initialization
come from [dcl.init]/17:
— If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
— [...]
— 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. [...]
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). [...] 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.
For both a2 and a3, the initializer is a braced-init-list, so we just do list-initialization. This ends up calling the B const& constructor.
For a1, the first sub-bullet doesn't apply - since the source type (B) is not the same or derived class of the destination type (A). So we go into the second sub-bullet point which involves considering conversion functions. There is one (A(B const&)) so we effectively rewrite the expression
A a1_new{A{B{}}};
Now typically, this extra copy will be elided. But you're explicitly prohibiting it, so the code cannot compile.
As to why the differentiation? I don't know. It seems like copy-initialization should simply be syntactic sugar for direct-initialization. In most cases, after all, it is...
(The following applies to C++11)To avoid rambling too much, A a2 = B(); has two stage : first, A temp object of type B is created when you write B(). Then, your function A(B const&) {} will not be invoked here with the role of directly initializing A because you use the copy-initialization syntax. If you want to invoke it to directly initialize A, you should write A a2(B()) instead, rather than using the copy-initialization syntax(the devil here is =). So, what's next? You get a temp object of type B, and you want to use it to initialize obj of type A, and now, you indirectly initialize A by converting B() to the type A. So your function A(B const&) {} is used as a type conversion rather than directly initializing A.
Because of conversion, a temp obj of type A is created, and then we need copy constructor to initialize a2 using that temporary obj.
In copy initialization, type-conversion-functions with explicit keyword can't be called. The design philosophy of explicit keyword is that you should directly invoke it. Combined with type-conversion-functions, those explicit attributed functions should not be used for implicit type conversion. In direct initialization, they can all be called even they are explicit.
To make things interesting, if you make your copy constructor and move constructor explicit, you can't even write T obj = T{}, but you can write T obj {T{}}. You can view copy and move ctor as a conversion too, only the destination and source type are of the same class.
I would like to provide you with further readings here. After that, read this question to know about copy-list-initialization and this question to learn about aggregate-types.

Spurious C++ destructor call under Visual Studio 2008 (absent under GCC)

Take the following (contrived) class hierarchy that prints to the console from the constructors and destructors:
#include <iostream>
class A {
public:
A() { std::cout << "A"; }
~A() { std::cout << "~A"; }
};
class B : public A {
public:
B() { std::cout << "B"; }
~B() { std::cout << "~B"; }
};
void func(A a) { }
int main() {
B b;
func(b);
std::cout << "X";
return 0;
}
Compiled under linux with gcc, it prints AB~AX~B~A as expected (the ~A printed before X is a result of the pass-by-value to func which creates a copy that is destructed when the function returns).
But compiled under windows with VS2008 it prints AB~A~AX~B~A - where does the extra ~A come from? It vanishes if the copy xtor is explicitly defined A(A& that) {}; or if the destructor is declared virtual (as it should be, arguably).
The comments suggest that MSVC 2008 uses a temporary that g++ doesn't, to pass the parameter. If so, this is a bug. From C++03 [dcl.init]/12:
The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and brace-enclosed initializer lists (8.5.1) is called copy-initialization and is equivalent to the form
T x = a;
Now here is the crucial bit. In T x = a;, if a is not a T or derived from T, then that is equivalent to T x = T(a);, and an extra temporary is conceptually used. (This temporary is eligible for copy-elision).
However, if a is a T or derived from T, then there must not be an extra temporary.It is the same as T x(a);.
In this question's code, since B derives from A, there must not be a temporary.
The supporting text in C++03 is under [dcl.init]/14 (I have highlighted the parts relevant to this question's code sample):
If the destination type is a (possibly cv-qualified) class type:
If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.
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(s) 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 destination type. 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 imple-
mentation 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.

In copy-initialization, is the call to the copy constructor explicit or implicit?

class AAA {
public:
explicit AAA(const AAA&) {}
AAA(int) {}
};
int main() {
AAA a = 1;
return 0;
}
In the above code, as I understand, though elided in most cases, the copy constructor is still semantically required to be called. My question is, is the call explicit or implicit? For a long time I have the conclusion in my mind that the call to AAA::AAA(int) is implicit but the call to the copy constructor is not. Today I accidentally got g++ to compile the above code and it reported error. (VC12 compiles OK.)
In section 8.5 of the standard:
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.
The bolded direct-initialize in the above quotes means the call to copy constructor is explicit, right? Is g++ wrong or my interpretation of the standard wrong?
Looks like this bug: g++ fails to call explicit constructors in the second step of copy initialization
g++ fails to compile the following code
struct X
{
X(int) {}
explicit X(X const &) {}
};
int main()
{
X x = 1; // error: no matching function for call to 'X::X(X)'
}
The second step of a copy initialization (see 8.5/16/6/2) is a
direct-initialization where explicit constructors shall be considered
as candidate functions.
Looks like copy constructor is never called . Only constructor is called . The following code may call copy constructor
AAA a = 1;
AAA ab = a;
Not sure why G++ is compiling it .