Why would std::initializer_list copy items? - c++

I always thought std::initializer_list is a lightweight proxy object which would only take const references from list items, instead of copy them.
But then I discovered that copy is actually performed in this case:
struct Test {
Test() {
std::cout << this << " default ctor" << std::endl;
}
Test(const Test&) {
std::cout << this << " copy ctor" << std::endl;
}
~Test() {
std::cout << this << " destructor" << std::endl;
}
};
int main() {
Test a;
Test b;
Test c;
std::cout << "for begin" << std::endl;
for(const auto& current : {a, b, c}) {
std::cout << "Current: " << &current << std::endl;
}
std::cout << "for end" << std::endl;
}
Output of above code:
0x63e5acda default ctor
0x63e5acdb default ctor
0x63e5acdc default ctor
for begin
0x63e5acdd copy ctor
0x63e5acde copy ctor
0x63e5acdf copy ctor
Current: 0x63e5acdd
Current: 0x63e5acde
Current: 0x63e5acdf
0x63e5acdf destructor
0x63e5acde destructor
0x63e5acdd destructor
for end
0x63e5acdc destructor
0x63e5acdb destructor
0x63e5acda destructor
Why would std::initializer_list copy items in this case, instead of just taking their references? Is there any "elegant" way to write things similar to for(auto&& x : {a, b, c}), but without copying existing items?

From the documentation on std::initializer_list:
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.

Related

this changes value in default and non-default constructor

I have a problem where I need to initialise a member object via a non-default constructor. However the compiler insists that I create a default constructor for the member object, which is called before the non-default constructor.
Consequently, the "this" pointer for the member object has different values in the default and non-default constructor but seems to stablise to the value in the default constructor.
I'm sure this is because I'm not using intialisation properly in C++, but I'm not sure what the correct way is to handle this.
Below is code which demonstrates the issue.
#include<iostream>
class my_object {
public:
my_object(int a) {
std::cout << "In non-default constructor for B\n";
std::cout << "Address of object is " << this << "\n\n";
}
my_object() {
std::cout << "In default constructor for B\n";
std::cout << "Address of object is " << this << "\n\n";
}
};
class my_first_object {
public:
my_first_object() {
std::cout << "In constructor for A\n";
std::cout << "The address of B is " << &B << "\n\n";
B = my_object(0);
std::cout << "We have just called the non-default constructor for B\n";
std::cout << "The address of B is " << &B << "\n\n";
}
my_object B;
};
int main() {
std::cout << "\n";
my_first_object A = my_first_object();
std::cout << "The address of A.B is " << &A.B << "\n";
return 0;
}
The output of this programme is
In default constructor for B
Address of object is 0x7ffee5d83ae8
In constructor for A
The address of B is 0x7ffee5d83ae8
In non-default constructor for B
Address of object is 0x7ffee5d83a90
We have just called the non-default constructor for B
The address of B is 0x7ffee5d83ae8
The address of A.B is 0x7ffee5d83ae8
You're loooking at this in two different objects - the second is created in the assignment
B = my_object(0);
which creates another my_object and assigns its value to B (which has already been created).
The only way to initialize members is to use the initializer list:
my_first_object() : B(0)
{
}
The reason for the compiler's insistence that you add a default constructor is that if you don't initialize a member in the initializer list, it is "default-initialized", and your code is equivalent to
my_first_object()
: B() // Initialization
{
B = my_first_object(0); // Assignment
}
Please see program here : http://cpp.sh/2oisg
You need to change, your my_first_object constructor like this:
my_first_object(int a = 0):B(a) {
std::cout << "In constructor for A\n";
std::cout << "The address of B is " << &B << "\n\n";
// B = my_object(0);
std::cout << "We have just called the non-default constructor for B\n";
std::cout << "The address of B is " << &B << "\n\n";
}
This way you are preventing a call to default constructor of my_object

vector elements allocated on stack?

#include <iostream>
#include <vector>
using namespace std;
struct A {
int i = 0;
};
void append(vector<A>& v) {
auto a = v.back(); // is a allocated on the stack? Will it be cleaned after append() returns?
++a.i;
v.push_back(a);
}
void run() {
vector<A> v{};
v.push_back(A{}); // is A{} created on the stack? Will it be cleaned after run() returns?
append(v);
for (auto& a : v) {
cout << a.i << endl;
}
}
int main() {
run();
return 0;
}
The code above prints as expected:
0
1
But I have two questions:
is A{} created on the stack? Will it be cleaned after run() returns?
is a allocated on the stack? Will it be cleaned after append() returns?
Update:
#include <iostream>
#include <vector>
using namespace std;
struct A {
int i = 0;
A() { cout << "+++Constructor invoked." << endl; }
A(const A& a) { cout << "Copy constructor invoked." << endl; }
A& operator=(const A& a) {
cout << "Copy assignment operator invoked." << endl;
return *this;
};
A(A&& a) { cout << "Move constructor invoked." << endl; }
A& operator=(A&& a) {
cout << "Move assignment operator invoked." << endl;
return *this;
}
~A() { cout << "---Destructor invoked." << endl; }
};
void append(vector<A>& v) {
cout << "before v.back()" << endl;
auto a = v.back();
++a.i;
cout << "before v.push_back()" << endl;
v.push_back(a);
cout << "after v.push_back()" << endl;
}
void run() {
vector<A> v{};
v.push_back(A{});
cout << "entering append" << endl;
append(v);
cout << "exited append" << endl;
for (auto& a : v) {
cout << a.i << endl;
}
}
int main() {
run();
return 0;
}
Output:
+++Constructor invoked.
Move constructor invoked.
---Destructor invoked.
entering append
before v.back()
Copy constructor invoked.
before v.push_back()
Copy constructor invoked.
Copy constructor invoked.
---Destructor invoked.
after v.push_back()
---Destructor invoked.
exited append
0
0 // I understand why it outputs 0 here. I omitted the actual work in my copy/move constructors overloads.
---Destructor invoked.
---Destructor invoked.
I updated the code in my question, adding the copy/move constructors. I found copy constructor was called 3 times in append. I understand auto a = v.back(); needs a copy, But the two other copies maybe should be avoided?
The C++ specification doesn't actually say.
With v.push_back(A{}) the A{} part creates a temporary object, which is then moved or copied into the vector, and then the temporary object is discarded.
Same with local variables, really, the "stack" is actually never mentioned by the C++ standard, it only tells how life-time should be handled. That a compiler might use a "stack" is an implementation detail.
With that said, most C++ compilers will use the "stack" to store local variables. Like for example the variable a in the append function. As for the temporary object created for v.push_back(A{}) you need to check the generated assembly code.
For the life-times, the life-time of the temporary object A{} ends as soon as the push_back function returns. And the life-time of a in the append function ends when the append function returns.
In this function
void append(vector<A>& v) {
auto a = v.back(); // is a allocated on the stack? Will it be cleaned after append() returns?
++a.i;
v.push_back(a);
}
the variable a has the automatic storage duration and is a local variable of the function. It will not be alive after exiting the function.
In this function
void run() {
vector<A> v{};
v.push_back(A{}); // is A{} created on the stack? Will it be cleaned after run() returns?
append(v);
for (auto& a : v) {
cout << a.i << endl;
}
}
again the variable v has the automatic storage duration and is a local variable of the function. When the function will finish its execution the variable will be destroyed. And all elements of the vector (that are placed in the heap) also will be destroyed due to the destructor of the vector.
Consider the following demonstrative program.
#include <iostream>
#include <vector>
struct A {
int i = 0;
};
int main()
{
std::vector<A> v;
std::cout << "&v = " << &v << "\n\n";
A a;
std::cout << "&a = " << &a << "\n\n";
v.push_back( a );
std::cout << "&v = " << &v << '\n';
std::cout << "&a = " << &a << '\n';
std::cout << "&v[0] = " << &v[0] << "\n\n";
++a.i;
v.push_back( a );
std::cout << "&v = " << &v << '\n';
std::cout << "&a = " << &a << '\n';
std::cout << "&v[0] = " << &v[0] << '\n';
std::cout << "&v[1] = " << &v[1] << "\n\n";
return 0;
}
Its output might look like
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v[0] = 0x55725232ee80
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v[0] = 0x55725232eea0
&v[1] = 0x55725232eea4
As you can see the addresses of the vector v and the object a looks similarly because they are allocated in the same outer block scope of the function and have the automatic storage duration.
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
And they are not changed when new values are pushed on the vector.
However the addresses of the elements of the vector as for example
&v[0] = 0x55725232ee80
&v[0] = 0x55725232eea0
&v[1] = 0x55725232eea4
have a different representation and can be changed when a new elements are added to the vector because the memory for them can be dynamically reallocated.
EDIT: After you updated your question then take into account that when a new element is added to the vector the elements of the vector can be reallocated calling the copy constructor. You can use the method reserve to reserve enough memory to avoid its reallocation and the method emplace_back.
is a allocated on the stack?
There is no such thing as "stack" storage in the language. a has automatic storage.
As far as language implementations are concerned, this typically means that the variable is probably stored in a register, or on stack, or nowhere.
Will it be cleaned after append() returns?
Yes. Automatic variables are destroyed automatically when they go out of scope.
is A{} created on the stack?
A{} is a temporary object. The language is a bit vague about the storage class of temporary objects, but it is clear about the lifetime.
Will it be cleaned after run() returns?
In this case, the temporary object is destroyed at the end of the full expression, which is before run returns.
vector elements allocated on stack?
No. Vector elements are created in dynamic storage.
Update
But the two other copies maybe should be avoided?
If your endgoal is to get a vector with two elements, you can avoid all of the copies like this:
std::vector<A> v(2);

initializing struct while using auto causes a copy in VS 2013

In the following code the line that creates nested object prints only "constructor" with gcc, but not with VS 2013:
#include <iostream>
using namespace std;
struct test {
test() { cout << "constructor" << endl; }
test(const test&) { cout << "copy constructor" << endl; }
test(test&&) { cout << "move constructor" << endl; }
~test() { cout << "destructor" << endl; }
};
struct nested {
test t;
// nested() {}
};
auto main() -> int {
// prints "constructor", "copy constructor" and "destructor"
auto n = nested{};
cout << endl;
return 0;
}
Output:
constructor
copy constructor
destructor
destructor
So I guess what happening here is that a temporary object gets copied into n. There is no compiler-generated move constructor, so that's why it's not a move.
I'd like to know if this is a bug or an acceptable behaviour? And why adding a default constructor prevents a copy here?
It's not auto that's the problem; the following will exhibit the same:
nested n = nested{};
The temporary is being direct-initialized with {}, and then n is being copy-initialized with that, because test is a class-type (in this case, has user-defined constructor).
An implementation is permitted to directly initialize the ultimate target (n), but isn't obligated to, so either is legal.
Lots (really, lots) of details in 8.5 and 8.5.1 of the Standard.
This is a failure of MSVC to do copy elision (I'd guess related to the brace-init-constructor). It's perfectly legal both ways.

Why does vector::push_back and emplace_back call value_type::constructor twice?

I have this class:
class Foo {
public:
Foo() {}
Foo(const Foo&){cout << "constructed by lvalue reference." <<endl; }
Foo(Foo&& ) {cout << "constructed by rvalue reference." << endl; }
};
then I insert into a vector:
Foo foo{};
vf.push_back(foo);
The output is surprising:
constructed by lvalue reference.
constructed by lvalue reference.
I assume it got copied when passing parameters, so I tried:
vf.push_back(move(foo));
and
vf.push_back(forward<Foo>(foo));
The output are slightly different due to move semantics but still calling constructor twice:
constructed by rvalue reference.
constructed by lvalue reference.
Why the constructors got called twice? How much performance does it impact? How can I avoid this?
I am using mingw-gcc-4.7.1 on Windows Vista
Total example:
#include <iostream>
#include <vector>
using namespace std;
class Foo {
public:
Foo() {}
Foo(const Foo&){cout << "constructed by lvalue reference." <<endl; }
Foo(Foo&& ) {cout << "constructed by rvalue reference." << endl; }
};
int main(int argc, char **argv, char** envp)
{
vector<Foo> vf;
cout << "Insert a temporary." << endl;
vf.emplace_back(Foo{});
Foo foo{};
cout << "Insert a variable." << endl;
vf.emplace_back(foo);
return 0;
}
Exact output:
Insert a temporary.
constructed by rvalue reference.
Insert a variable.
constructed by lvalue reference.
constructed by lvalue reference.
When you insert new items in a vector the vector may have to allocate more memory to fit those objects. When that happens it needs to copy all it's elements to the new memory location. That will invoke the copy constructor. So when you insert your element you're getting the constructor for that new element and the constructor when copying the previous element.
vector<Foo> vf;
cout << "Insert a temporary." << endl;
vf.emplace_back(Foo{});
What happens above is that a temporary Foo is created.
This temporary is then used to construct a Foo within the vector. So a "constructed by rvalue reference" is what you asked for.
If you wish to simply construct the Foo in place, try:
vs.emplace_back();
Next:
Foo foo{};
cout << "Insert a variable." << endl;
vf.emplace_back(foo);
here you construct a non-temporary foo. You then instruct the std::vector to construct a new element at the end of the list.
The interesting thing is that you get two construct by lvalue reference. The second seems to be caused by the resize. Why the resize causes you to be construced by lvalue reference, instead of rvalue reference, is a trick: if your move constructor is not marked noexcept, std::vector falls back on copy instead of move!
Here is a live example illustrating the above principles:
#include <iostream>
#include <vector>
using namespace std;
class Foo {
public:
Foo() {}
virtual ~Foo() {}
Foo(const Foo&){cout << "constructed by lvalue reference." <<endl; }
Foo(Foo&){cout << "constructed by non-const lvalue reference." <<endl; }
Foo(Foo&& ) noexcept {cout << "constructed by rvalue reference." << endl; }
};
int main(int argc, char **argv, char** envp)
{
vector<Foo> vf;
cout << "Insert a temporary. One move:" << endl;
vf.emplace_back(Foo{});
cout << "Insert a temporary(2). Two moves:" << endl;
vf.emplace_back(Foo{});
cout << "Resize with temporary(3). Two moves:" << endl;
vf.resize(10);
vector<Foo> vf2;
Foo foo{};
cout << "Insert a variable. One copy:" << endl;
vf2.emplace_back(foo);
cout << "Insert a variable(2). One move, one copy:" << endl;
vf2.emplace_back(foo);
cout << "Resize with variable(3). Two moves:" << endl;
vf2.resize(10);
vector<Foo> vf3;
cout << "Insert a nothing. No copy or move:" << endl;
vf3.emplace_back();
cout << "Insert a nothing(2). One move:" << endl;
vf3.emplace_back();
cout << "Resize with nothing(3). Two moves:" << endl;
vf3.resize(10);
}
A common implementation of std::vector::push_back looks like this:
void push_back(value_type _Val)
{ // insert element at end
insert_n(size(), 1, _Val);
}
As you can see, the input parameter is passed-by-value (so it is copied) in both the push_back declaration and the insert_n declaration. Hence, the copy-constructor is called twice.
After cleaning up your syntax:
#include <iostream>
#include <vector>
using namespace std;
class Foo
{
public:
Foo() {}
Foo(const Foo&) {cout << "constructed by lvalue reference." <<endl; }
Foo(Foo&&) {cout << "constructed by rvalue reference." << endl; }
};
int main()
{
vector<Foo> vf;
cout << "Size = " << vf.size() << endl;
cout << "Capacity = " << vf.capacity() << endl;
cout << "Insert a temporary" << endl;
vf.push_back(Foo()); // this is still very discouraged syntax
Foo foo;
cout << "Insert a variable." << endl;
vf.push_back(foo);
return 0;
}
You get the following output:
Size = 0
Capacity = 0
Insert a temporary
constructed by rvalue reference.
Insert a variable.
constructed by rvalue reference.
constructed by lvalue reference.
Press any key to continue . . .
In this example, I'm using a standard version of std::vector (which passes by const-reference or by reference-to-reference). The initial push_back call creates a capacity of 1 (and a size of 1). The 2nd call creates a new block of memory, moves the first item, and copies the second (the newly added) item.
In terms of performance, you aren't going to take a big hit for small reallocations. There are a few different common memory models used (the one with Visual Studio grows your capacity exponentially each time it has to grow to decrease the need for it in the future). If you know you will be starting with 100 elements, you should reserve space when you create your vector so allocation only happens once, which will also prevent the need to move existing elements when inserting new elements (due to the fact that you won't be exceeding your capacity multiple times).

Why only one object gets constructed but multiple objects are destroyed when using functor?

The following example, beats me. I've been so far thinking, that when functor is being used, the object gets constructed once and the same object is used multiple times, when used with for_each algorithm and that seems to be correct.
However, even though, only one object gets constructed, but multiple objects are destroyed. Now, this beats me.
class print
{
public:
void operator()(int i)
{
std::cout << i << std::endl;
}
print()
{
std::cout << "Constructor " << std::endl;
}
~print()
{
std::cout << "Destructor" << std::endl;
}
};
int main()
{
std::vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
std::cout << "After assigning values " << std::endl;
for_each(v.begin() , v.end() , print());
std::cout << "After printing values " << std::endl;
}
The output is as follows
After assigning Values
Constructor
10
20
30
Destructor
Destructor
Destructor
After printing values.
How is this possible?
Don't forget about the copy constructor (the Rule of Three can help you to remember this):
class print
{
public:
void operator()(int i)
{
std::cout << i << std::endl;
}
print()
{
std::cout << "Constructor " << std::endl;
}
print(const print& other) {
std::cout << "Copy Constructor " << std::endl;
}
~print()
{
std::cout << "Destructor" << std::endl;
}
};
int main()
{
std::vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
std::cout << "After assigning values " << std::endl;
for_each(v.begin() , v.end() , print());
std::cout << "After printing values " << std::endl;
}
Output:
After assigning values
Constructor
Copy Constructor
10
20
30
Copy Constructor
Destructor
Destructor
Destructor
After printing values
Here is how I have for_each on my system:
template<class _InIt,
class _Fn1> inline
_Fn1 _For_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{ // perform function for each element
for (; _First != _Last; ++_First)
_Func(*_First);
return (_Func); // a copy could be created here (C3)
}
template<class _InIt,
class _Fn1> inline
_Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{ // perform function for each element
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Func);
return (_For_each(_Unchecked(_First), _Unchecked(_Last), _Func)); // a copy created here (C2)
}
So, essentially, this is how it could look
for_each(v.begin() , v.end() , print()); // print functor created (constructor)
// a copy created here (C1)
Now, this is completely upto the implementation if copy elision is done or not. The Standard does allow that latitude to elide away the copy constructor e.g. C3 may be eliminated.
A good way to control gcc behavior here is the -fno-elide-constructors which ensures that code does not elide away the copy constructor
You are only printing "Constructor" from the default constructor. Instances of your type can come into existance from the copy-constructor as well. Copies might be being made through the call stack. Unless you specify otherwise you get a default copy constructor for free. You can add a copy constructor and also print out "Constructor" if you want to verify this.
No matter how it's constructed, you will still see the one-and-only destructor fire and print-out "Destructor".
I would say that the for_each created copies of the print() functor which calls the implicit copy constructor defined by the compiler which doesn't appear in your code (not the same as your defined constructor above).