cppreference says:
The underlying array is a temporary array of type const T[N], in which
each element is copy-initialized (except that narrowing
conversions are invalid) from the corresponding element of the
original initializer list. The lifetime of the underlying array is the
same as any other temporary object, except that initializing an
initializer_list object from the array extends the lifetime of the
array exactly like binding a reference to a temporary (with the same
exceptions, such as for initializing a non-static class member). The
underlying array may be allocated in read-only memory.
What's the reasoning behind this decision? Why is moving not ok?
What about copy-ellision?
struct A { A(const A&){ std::cout << "Oh no, a copy!\n"; } };
struct B { B(std::initializer_list<A> il); };
int main()
{
B b{ A{} };
return 0;
}
My compiler ellides the copy. But are these copies guaranteed to be ellided?
"Copy initialization" in C++ doesn't mean things will necessarily be copied. It's just a formal name for the constraints under which initialization will occur. For instance, when copy initializing, explicit c'tors are not candidates. So the following code will be ill-formed
#include <iostream>
#include <initializer_list>
struct A {
explicit A() = default;
A(const A&){ std::cout << "Oh no, a copy!\n"; }
};
struct B { B(std::initializer_list<A> il); };
int main()
{
B b{ {} };
return 0;
}
The single member in the list needs to be copy-initialized from {}, which entails calling the default c'tor. However, since the c'tor is marked explicit, this initialization cannot happen.
Copy elision is certainly possible pre-C++17, and is mandatory in C++17 onward in certain contexts. In your example, under a C++17 compiler, since you provide an initializer that is a prvalue (a pure rvalue, not an object), the initialization rules of C++ mandate that the target is initialized directly, without intermediate objects created. Even though the context is called "copy initialization", there are no superfluous objects.
Related
I am wondering that object construction with assignment operator works, never seen that before I saw this question:
return by value calls copy ctor instead of move
Reduced example code:
class A
{
public:
int x;
A(int _x):x(_x){ std::cout << "Init" << std::endl;}
void Print() { std::cout << x << std::endl; }
A& operator = ( const int ) = delete;
};
int main()
{
A a=9;
a.Print();
}
Is writing of
A a(9);
A a{9};
A a=9;
all the same?
This has nothing to do with assignment operator, it's initialization, more precisely copy initialization, which just uses the equals sign in the initializer.
when a named variable (automatic, static, or thread-local) of a non-reference type T is declared with the initializer consisting of an equals sign followed by an expression.
For A a = 9; the approriate constructor (i.e. A::A(int)) will be invoked to construct a. 1
A a(9); is direct initialization, A a{9}; is direct list initialization (since C++11), they all cause the A::A(int) to be invoked to construct the object for this case. 2
1 Before C++17 the appropriate move/copy constructor is still required conceptually. Even though it might be optimized out but still has to be accessible. Since C++17 this is not required again.
2 Note that there're still subtle differences among these initialization styles, they may lead to different effects in some specialized cases.
#include <iostream>
#include <string>
#include <array>
class C {
private:
std::string a;
std::string b;
std::string c;
public:
C(std::string a_, std::string b_, std::string c_) : a{a_},b{b_},c{c_} {}
~C(){};
C(const C&) =delete;
C(const C&&) =delete;
const C& operator=(const C&) =delete;
const C& operator=(const C&&) =delete;
};
std::array<C,2> array = {C("","",""),C("","","")};
int main()
{}
this won't compile (Android Studio with NDK and clang) with a "call to deleted constructor of c" error. I know that I can e.g. use a std::vector and emplace_back() to construct an element directly inside the container, but in my code I want to only use fixed-sized containers and non-copyable/moveable objects for optimization. I'm probably missing sth basic here, but isn't there a way to initialize the std::array without first having to construct the individual elements and then copy them there?
You can use brace enclosed initializers instead of temporary c objects:
std::array<c,2> array = {{{"",""},{"",""}}};
or
std::array<c,2> array{{{"",""},{"",""}}};
It would become possible since C++17, from that for some specified cases copy elision is guaranteed.
Under the following circumstances, the compilers are required to omit
the copy- and move- constructors of class objects even if copy/move
constructor and the destructor have observable side-effects:
In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the
class of the destination, the initializer expression is used to
initialize the destination object:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
And for these cases, copy/move constructor is not required to be accessible.
When copy-elision takes place (until C++17) In those cases where
copy-elision is not guaranteed, if it takes place (since C++17) and
the copy-/move-constructor is not called, it must be present and
accessible (as if no optimization happened at all), otherwise the
program is ill-formed.
LIVE
I was using somebody else's class which was acting odd when I pushed it into a vector. It involves a member variable which is a reference to another member variable. Here is the smallest self-contained example:
#include <iostream>
#include <vector>
class Myclass {
public:
Myclass() : a(1.0) {}
float a;
float &a_ref = a;
void addOne() {
a = a + 1.0;
}
};
int main() {
Myclass instance1;
instance1.addOne();
//prints 2:
std::cout << "instance.a_ref is " << instance1.a_ref << std::endl;
std::vector<Myclass> vec;
Myclass instance2;
vec.push_back(instance2);
vec.at(0).addOne();
//prints 1;
std::cout << "vec.at(0).a_ref is " << vec.at(0).a_ref << std::endl;
return 0;
}
I was compiling with g++ and -std=c++11, so I didn't notice the problem for a while. I see now the issue is probably to do with the synthesised copy constructor and the reference member. But what I'm not sure about is:
Why is there different behaviour when the object is in a vector ?
Why does g++ not give any warnings about this, using c++11 standard ?
Bonus question because I'm curious:
What is initialized first, a or a_ref?
The problem is indeed with the defaulted copy constructor. The defaulted copy constructor initialises all members from the members of the source object. That is, the defaulted copy constructor is identical to this:
Myclass(const Myclass &src) :
a(src.a),
a_ref(src.a_ref)
{}
The defaulted copy constructor initialises all members, so it ignores any in-class initialisers.
This is also why pushing into a vector causes the problem. vec.at(0) was created as a copy of instance2, which means vec.at(0).a_ref refers to instance2.a. You could easily verify this by printing their addresses (live example).
The implicitly defined copy/move constructor:
[...] performs a performs a memberwise copy/move of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. [...]
In particular, reference members are direct-initialized to refer to the same object the corresponding reference member in the source object refers to.
So in your case, vec.at(0).a_ref refers to the member a of instance2.
This is not detected by the compiler because in general reference members are expected to refer to a longer-lived object outside the class.
I have a class B which contains a vector of class A. I want to initialize this vector through the constructor. Class A outputs some debug info so I can see when it is constructed, destructed, copied or moved.
#include <vector>
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A::A" << endl; }
~A() { cout << "A::~A" << endl; }
A(const A& t) { cout <<"A::A(A&)" << endl; }
A(A&& t) { cout << "A::A(A&&)" << endl; }
};
class B {
public:
vector<A> va;
B(const vector<A>& va) : va(va) {};
};
int main(void) {
B b({ A() });
return 0;
}
Now when I run this program (Compiled with GCC option -fno-elide-constructors so the move constructor calls are not optimized away) I get the following output:
A::A
A::A(A&&)
A::A(A&&)
A::A(A&)
A::A(A&)
A::~A
A::~A
A::~A
A::~A
A::~A
So instead of just one instance of A the compiler generates five instances of it. A is moved two times and it is copied two times. I didn't expect that. The vector is passed by reference to the constructor and then copied into the class field. So I would have expected a single copy-operation or even just a move operation (because I hoped the vector I pass to the constructor is just a rvalue), not two copies and two moves. Can someone please explain what exactly happens in this code? Where and why does it create all these copies of A?
It might be helpful to go through the constructor calls in reverse order.
B b({ A() });
To construct a B, the compiler must call B's constructor that takes a const vector<A>&. That constructor in turn must make a copy of the vector, including all of its elements. That's the second copy ctor call you see.
To construct the temporary vector to be passed to B's constructor, the compiler must invoke the initializer_list constructor of std::vector. That constructor, in turn, must make a copy of what's contained in the initializer_list*. That's the first copy constructor call you see.
The standard specifies how initializer_list objects are constructed in §8.5.4 [dcl.init.list]/p5:
An object of type std::initializer_list<E> is constructed from an
initializer list as if the implementation allocated an array of N
elements of type const E**, where N is the number of elements in the
initializer list. Each element of that array is copy-initialized with
the corresponding element of the initializer list, and the
std::initializer_list<E> object is constructed to refer to that array.
Copy-initialization of an object from something of the same type uses overload resolution to select the constructor to use (§8.5 [dcl.init]/p17), so with an rvalue of the same type it will invoke the move constructor if one is available. Thus, to construct the initializer_list<A> from the braced initializer list, the compiler will first construct an array of one const A by moving from the temporary A constructed by A(), causing a move constructor call, and then construct the initializer_list object to refer to that array.
I can't figure out where the other move in g++ comes from, though. initializer_lists are usually nothing more than a pair of pointers, and the standard mandates that copying one doesn't copy the underlying elements. g++ seems to call the move constructor twice when creating an initializer_list from a temporary. It even calls the move constructor when constructing an initializer_list from a lvalue.
My best guess is that it's implementing the standard's non-normative example literally. The standard provides the following example:
struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };
The initialization will be implemented in a way roughly equivalent to
this:**
const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
assuming that the implementation can construct an initializer_list object with a pair of pointers.
So if you take this example literally, the array underlying the initializer_list in our case will be constructed as if by:
const A __a[1] = { A{A()} };
which does incur two move constructor calls because it constructs a temporary A, copy-initializes a second temporary A from the first one, then copy-initializes the array member from the second temporary. The normative text of the standard, however, makes clear that there should only be one copy-initialization, not two, so this seems like a bug.
Finally, the first A::A comes directly from A().
There's not much to discuss about the destructor calls. All temporaries (regardless of number) created during the construction of b will be destructed at the end of the statement in reverse order of construction, and the one A stored in b will be destructed when b goes out of scope.
* The initializer_list constructors of standard library containers are defined as being equivalent to invoking the constructor taking two iterators with list.begin() and list.end(). Those member functions return a const T*, so it can't be moved from. In C++14, the backing array is made const, so it's even clearer that you can't possibly move from it or otherwise change it.
** This answer originally quoted N3337 (the C++11 standard plus some minor editorial changes), which has the array having elements of type E rather than const E and the array in the example being of type double. In C++14, the underlying array was made const as a result of CWG 1418.
Try split the code a little to better understand the behavior:
int main(void) {
cout<<"Begin"<<endl;
vector<A> va({A()});
cout<<"After va;"<<endl;
B b(va);
cout<<"After b;"<<endl;
return 0;
}
The output is similar (note the -fno-elide-constructors is used)
Begin
A::A <-- temp A()
A::A(A&&) <-- moved to initializer_list
A::A(A&&) <-- no idea, but as #Manu343726, it's moved to vector's ctor
A::A(A&) <-- copied to vector's element
A::~A
A::~A
A::~A
After va;
A::A(A&) <-- copied to B's va
After b;
A::~A
A::~A
Consider this:
The temporary A is instanced: A()
That instance is moved to the initializer list: A(A&&)
The initializer list is moved to the vector ctor, so its elements are moved: A(A&&).
EDIT: As T.C. noticed, initializer_list elements are not moved/copied for initializer_list moving/copying. As his code example shows, seems like two rvalue ctor calls are used during initializer_list initialization.
The vector element is initialized by value, instead of by move (Why?, I'm not sure): A(const A&) EDIT: Again, is not the vector but the initializer list
Your ctor gets that temporal vector and copies it (Note your vector initializer), so
the elements are copied: A(const A&)
A::A
The constructor is executed, when the temporary object is created.
first A::A(A&&)
The temporary object is moved into the initialization list (which is also rvalue).
second A::A(A&&)
The initialization list is moved into vector's constructor.
first A::A(A&)
The vector is copied because the B's constructor takes the lvalue, and a rvalue is passed.
second A::A(A&)
Again, the vector is copied when creating the B's member variable va.
A::~A
A::~A
A::~A
A::~A
A::~A
Destructor is called for every rvalue and lvalue (whenever the constructor, copy or move constructors are called, destructor is executed when the objects gets destroyed).
Lets say we have the following code:
#include <iostream>
#include <string>
struct A
{
A() {}
A(const A&) { std::cout << "Copy" << std::endl; }
A(A&&) { std::cout << "Move" << std::endl; }
std::string s;
};
struct B
{
A a;
};
int main()
{
B{A()};
}
Here, I believe struct A is not an aggregate, as it has both non-trivial constructors and also a std::string member which I presume is not an aggregate. This presumably means that B is also not an aggregate.
Yet I can aggregate initialize B. In addition this can be done without either the copy nor move constructor being called (e.g. C++0x GCC 4.5.1 on ideone).
This behavior seems like a useful optimization, particularly for composing together large stack types that don't have cheap moves.
My question is: When is this sort of aggregate initialization valid under C++0x?
Edit + follow up question:
DeadMG below answered with the following:
That's not aggregate initialization at all, it's uniform initialization, which basically in this case means calling the constructor, and the no copy or move is probably done by RVO and NRVO.
Note that when I change B to the following:
struct B
{
A a;
B(const A& a_) : a(a_) {}
B(A&& a_) : a(std::move(a_)) {}
};
A move is performed.
So if this is just uniform initialization and just calling the constructor and doing nothing special, then how do I write a constructor that allows the move to be elided?
Or is GCC just not eliding the move here when it is valid to do so, and if so, is there a compiler and optimization setting that will elide the move?
According to the new standard, clause 8.5.1 (Aggretates), a sufficiently simple type (e.g. no user-defined constructors) qualifies as an aggregate. For such an aggregate Foo, writing Foo x{a, b, ... }; will construct the members from the list items.
Simple example:
struct A
{
std::unordered_map<int, int> a;
std::string b;
std::array<int,4> c;
MyClass d; // Only constructor is MyClass(int, int)
};
// Usage:
A x{{{1,-1}, {12, -2}}, "meow", {1,2,3,4}, MyClass(4,4)};
// Alternative:
A x{{{1,-1}, {12, -2}}, "meow", {1,2,3,4}, {4,4}};
The object x is constructed with all the relevant constructors executed in place. No maps or strings or MyClasses ever get copied or moved around. Note that both variants at at the bottom do the same thing. You can even make MyClass's copy and move constructors private if you like.
That's not aggregate initialization at all, it's uniform initialization, which basically in this case means calling the constructor, and the no copy or move is probably done by RVO and NRVO.