I'm reading Prata's C++ book, and when talking about copy constructors, it says that this constructor is invoked when:
Initializing a class object to the a class object of the same type.
Passing an object by value to a function.
Returning an object by value from a function.
Let say that we are working with a Vector class.
For understanding sakes, throughout the examples in the current chapter we included in the constructors/destructor definitions string outputs, to indicate which and when each of them is called.
For instance, if we have
int main()
{
Vector v1; // print "Default constructor called"
Vector v2(9, 5); // print "Constructor called"
Vector v3 = v1; // print "Copy Constructor called"
return 0;
}
And Destructor called would be printed in this case 3 times, when exiting main().
In order to check the 3 points above I've been playing with a dumb_display() function, changing the type of the formal parameter/return value. In doing so, I got confused about what is indeed happening under the hood.
Vector dumb_display(Vector v)
{
Vector test(45, 67);
cout << "In dumb_display() function" << endl;
cout << v << endl;
return v;
}
Here:
Every time we return by value the passed argument as in the above function (argument either passed by value or reference) the copy constructor gets called.
It makes sense. It satisfies point 3.
Every time we return an object defined in the function's body (e.g., changing return v; by return test;), the copy constructor isn't called.
Point 3 isn't satisfied.
I'm having a hard time trying to understand this behaviour.
I don't know whether this is correct, but I think (since an automatic storage duration object is created once for the duration of the function call) once test is created it, hasn't have to be created again, because the object "is already there". This brings the question:
Why does returning the passed argument call the copy constructor twice? Why does the same object have to be created twice for the duration of the call to the function?
#include <vector>
#include <type_traits>
#include <tuple>
#include <iostream>
using namespace std;
struct S {
S(){
cout << "default constructor" << endl;
}
S(S const &) {
cout << "copy constructor" << endl;
}
S(S &&) {
cout << "move constructor" << endl;
}
S & operator=(S const &) {
cout << "copy assignment" << endl;
return *this;
}
S & operator=(S &&) {
cout << "move assignment" << endl;
return *this;
}
};
S f() {
S s2;
cout << "In f()" << endl;
return s2;
}
S f2(S s) {
cout << "In f2()" << endl;
return s;
}
int main() {
cout << "about to call f" << endl;
S s2 = f();
(void)s2;
cout << endl << "about to call f2" << endl;
S s3 = f2(s2);
(void)s3;
}
results in:
about to call f
default constructor
In f()
about to call f2
copy constructor
In f2()
move constructor
In f(), the object is default constructed and return value optimization is used to actually construct it in place where it will actually end up -- in the s2 variable in main. No copy/move constructors are called.
In f2(), a copy is made for the input parameter for the function. That value is then moved into the variable s3 in main, again with return return value optimization.
live: https://wandbox.org/permlink/kvBHBJytaIuPj0YN
If you turn off return value optimization, you will see the results that you would expect from what your book says:
live: https://wandbox.org/permlink/BaysuTYJjlJmMGf6
Here's the same two examples without move operators if that's confusing you:
live: https://wandbox.org/permlink/c0brlus92psJtTCf
and without return value optimization:
live: https://wandbox.org/permlink/XSMaBnKTz2aZwgOm
it's the same number of constructor calls, just using the (potentially slower) copy constructor instead of the move constructor.
The copy constructor is called twice because it is first copied from the function into the temperary value (which is represented by the function call and is what the returned value is, then copied into the variable, requiring two copies. Since this is not very efficient, there is also a "move" constructor, which is only needed once.
Related
I know ordinary std::vector::push_back() will copy the object. I hope this code would only destruct a only once, using std::move() and A(A&&) noexcept to avoid copying. But it doesn't seem to work.
Is there any way that I can construct an object before push_back() and move it into a vector perfectly?
#include <bits/stdc++.h>
using namespace std;
class A {
public:
A() { std::cout << "construct" << this << '\n'; }
A(A&&) noexcept { std::cout << "move" << this << "\n"; }
A(const A&) = delete;
~A() { std::cout << "destruct" << this << '\n'; }
};
std::vector<A> as;
void add(A&& a) {
std::cout << "add 1\n";
as.push_back(std::move(a));
std::cout << "add 2\n";
}
int main() {
add(A());
std::cout << "main2\n";
return 0;
}
Output:
construct0x16d20b1fb
add 1
move0x151e068b0
add 2
destruct0x16d20b1fb
main2
destruct0x151e068b0
I hope this code would only destruct a only once, using std::move() and A(A&&) noexcept to avoid copying.
Using Move constructor perverts copying but doesn't prevent creating new objects, you are creating an inline object with default constructor "cout : construct0x16d20b1fb" and then from that object's data your going to create a 'NEW' object and calling move constructor will
transform the ownership of the object's data/resources to the new object that is being 'Constructed' so that explain the "cout : move0x151e068b0" then the line is finished so your inline object is destroyed "cout " destruct0x16d20b1fb" then your program finishes "cout : destruct0x151e068b0" your object that made via move constructor is destroyed.
you are expecting the behavior of pointers from your vector of "Objects" which is supposed to hold actual objects not pointer to other objects so it needs to containt objects and each of objects that are in that vector has a different address, unless you create your object and use a vector pointers to objects.
By the way std::move is but a cast, it casts to rvalue references so when you are capturing with rvalue reference there's no need to cast it again, you should be using it like this:
A a_object1;
vector.push_back(std::move(a_object1));
and it will use the move constructor. although in this case it will cast it implicitly because you are capturing only by rvalue reference in add function unless you add an overload to this function that also takes reference there's no need.
I am trying to create a wrapper function that has the exact same interface as the function that it wraps, with zero runtime cost overhead.
In the example code below, is it possible to design my_function_wrapped in a way so that it has the same interface as my_function, so that calling it with the exact same arguments as to my_function always yield the same results?
#include <iostream>
using namespace std;
struct SMyStruct
{
SMyStruct() { cout << "SMyStruct constructed" << std::endl;}
SMyStruct(SMyStruct&& Other) { cout << "SMyStruct moved" << std::endl;}
SMyStruct(const SMyStruct& Other) { cout << "SMyStruct copied" << std::endl; }
~SMyStruct() {cout << "SMyStruct destroyed" << std::endl;}
};
void my_function(SMyStruct Arg)
{
}
template<typename T>
void my_function_wrapped(T&& Arg)
{
my_function(std::forward<T>(Arg));
// Some extra logic here that doesn't use Arg
}
int main()
{
cout << "-----------------------------" << endl;
cout << "Direct call to my_function:" << endl;
cout << "-----------------------------" << endl;
my_function(SMyStruct());
cout << "-----------------------------" << endl;
cout << "Wrapped call to my_function:" << endl;
cout << "-----------------------------" << endl;
my_function_wrapped(SMyStruct());
return 0;
}
This program outputs:
-----------------------------
Direct call to my_function:
-----------------------------
SMyStruct constructed
SMyStruct destroyed
-----------------------------
Wrapped call to my_function:
-----------------------------
SMyStruct constructed
SMyStruct moved
SMyStruct destroyed
SMyStruct destroyed
I realize that copy/move is elided on the first call to my_function because SMyStruct() is a prvalue. Is it possible to wrap this call in my_function_wrapped and still get the elided copy/move? Is there any zero-cost way to abstract away the call?
godbolt-link to the code: https://godbolt.org/z/joGTTe64f
Thanks!
No, it is not possible to chain copy elision of prvalues through function calls like this.
Copy elision only works because the caller can construct the function parameter knowing where it needs to place it from the declaration of the function and the calling convention used.
The original caller doesn't know that you are going to simply forward the argument to another function in the body and therefore it cannot know that it is supposed to construct the object into the deeper stack frame. C++ is also designed in such a way that functions can be compiled individually only having to know the declarations of other functions (constant expression evaluation aside). Definitions of the functions don't even have to be available where a call happens.
Allowing this would require some additional language feature to annotate a function declaration to inform callers where they have to construct the parameter and I think it would be difficult to find a good specification for such a feature.
What you can do is pass the arguments for your constructor, or more generally a callable object which creates your prvalue, around, e.g.
template<typename F>
void my_function_wrapped(F&& f)
{
my_function(std::invoke(std::forward<F>(f)));
}
//...
my_function_wrapped([]{ return SMyStruct(); });
The lambda can capture arguments to the constructor if needed.
(Note however that all of this requires C++17. You also tagged C++14, but in C++14 there was no guaranteed copy elision in any of the situations under discussion anyway.)
It depends on your real case (or if there is a real case to begin with), though in your example there is really no point in passing the SMyStruct to the wrapper to forward it to the actual function (because SMyStruct has no state). If instead you forward parameters for the constructor you get desired output:
template<typename...T>
void my_function_wrapped(T&&... Arg)
{
my_function(SMyStruct(std::forward<T>(Arg)...));
}
Live Demo
I am trying to get the grasp of rvalue references and move semantics with a simple self-made example but I can't understand a specific part. I have created the following class:
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
I tried the following experiments to see if I can predict how the constructors/operators are going to be called:
A a1(1) - The default constructor is going to be called.
PREDICTED.
A a2 = a1 - The copy constructor is going to be called. PREDICTED.
a1 = a2 - The copy assignment operator is going to be called.
PREDICTED.
Now, I created a simple function that just returns an A object.
A helper() {
return A(1);
}
A a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. The move
constructor is not going to be called due to RVO. PREDICTED.
a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. Then, the move
assignment operator is going to be called. PREDICTED.
Now comes the part I don't understand. I created another function that is completely pointless. It takes an A object by value and it just returns it.
A helper_alt(A a) {
return a;
}
A a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then the move
constructor. PREDICTED.
a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then I thought that
the move assignment operator is going to be called BUT as I saw,
first, the move constructor is called and then the move assignment
operator is called. HAVE NO IDEA.
Please, if any of what I said is wrong or you feel I might have not understood something, feel free to correct me.
My actual question: In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?
Congratulations, you found a core issue of C++!
There are still a lot of discussions around the behavior you see with your example code.
There are suggestions like:
A&& helper_alt(A a) {
std::cout << ".." << std::endl;
return std::move(a);
}
This will do what you want, simply use the move assignment but emits a warning from g++ "warning: reference to local variable 'a' returned", even if the variable goes immediately out of scope.
Already other people found that problem and this is already made a c++ standard language core issue
Interestingly the issue was already found in 2010 but not solved until now...
To give you an answer to your question "In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?" is, that also C++ committee does not have an answer until now. To be precise, there is a proposed solution and this one is accepted but until now not part of the language.
From: Comment Status
Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.
consider the below example. I have compiled the sample code using -fno-elide-constructors flag to prevent RVO optimizations:
g++ -fno-elide-constructors -o test test.cpp
#include<iostream>
using namespace std;
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
A a_global(1);
A helper_alt(A a) {
return a;
}
A helper_a_local(A a) {
A x(1);
return x;
}
A helper_a_global(A a) {
return a_global;
}
int main(){
A a1(1);
A a4(4);
std::cout << "================= helper_alt(a1) ==================" << std::endl;
a4 = helper_alt(a1);
std::cout << "=============== helper_a_local() ================" << std::endl;
a4 = helper_a_local(a1);
std::cout << "=============== helper_a_global() ================" << std::endl;
a4 = helper_a_global(a1);
return 0;
}
This will result in the below output:
Def constructor
Def constructor
Def constructor
================= helper_alt(a1) ==================
Copy constructor
Move constructor
Move Assignment
=============== helper_a_local() ================
Copy constructor
Def constructor
Move constructor
Move Assignment
=============== helper_a_global() ================
Copy constructor
Copy constructor
Move Assignment
In simple words, C++ constructs a new temporary object (rvalue) when the return type is not a reference, which results in calling Move or Copy constructor depending on the value category and the lifetime of the returned object.
Anyway, I think the logic behind calling the constructor is that you are not working with reference, and returned identity should be construed first, either by copy or move constructor, depending on the returned value category or lifetime of the return object. As another example:
A helper_move_vs_copy(A a) {
// Call the Copy Constructor
A b = a;
// Call the Move Constructor, Due to the end of 'a' lifetime
return a;
}
int main(){
A a1(1);
A a2(4);
std::cout << "=============== helper_move_vs_copy() ================" << std::endl;
helper_move_vs_copy(a1);
return 0;
}
which outputs:
Def constructor
Def constructor
=============== helper_move_vs_copy() ================
Copy constructor
Copy constructor
Move constructor
From cppreference:
an xvalue (an “eXpiring” value) is a glvalue that denotes an object whose resources can be reused;
At last, it is the job of RVO to decrease unnecessary moves and copies by optimization of the code, which can even result in an optimized binary for basic programmers!
class Entity
{
public:
int a;
Entity(int t)
:a(t)
{
std::cout << "Constructor !" << std::endl;
}
~Entity()
{
std::cout << "Destructor !" << std::endl;
}
Entity(Entity& o)
{
std::cout << "Copied !" << std::endl;
this->a = o.a;
}
};
Entity hi()
{
Entity oi(3);
return oi;
}
int main()
{
{
Entity o(1);
o = hi();
}
std::cin.get();
}
OUTPUT:
Constructor !
Constructor !
Copied !
Destructor !
Destructor !
Destructor !
I created two objects and I copied one, So three constructors and three destructors.
Your "Copied!" line in the output is coming from the copy constructor, so you're creating three objects, not just two (then you're destroying all three, as expected).
Note that a copy constructor should normally take its argument by const reference. At a guess, you may be using Microsoft's C++ compiler, which will bind a temporary to a non-const reference.
Also note that if you turn on optimization, you can probably expect to see just two constructors and two destructors, with no copy construction happening. With a new enough (C++17) compiler, that should happen even if you don't turn on optimization (copy elision has become mandatory).
This is a trivial question
Can any one explain the reason for three destructor?
when you call
o=hi();
your function is called which makes an object of type Entity , which in return calls the constructor.
This is where you get one extra constructor message
Replace your Entity(int t) contructor by this
Entity(int t)
:a(t)
{
std::cout << "Constructor created with integer "<< a << std::endl;
}
You will see which constructors were called when you run the code.
I was wondering why the vector templates perform two allocations, when only one seems to be
necessary.
For example this:
#include <vector>
#include <iostream>
class A {
public:
A(const A &a) {
std::cout << "Calling copy constructor " << this << " " << &a << "\n";
}
A() {
std::cout << "Calling default constructor " << this << "\n";
}
~A() {
std::cout << "Calling destructor " << this << "\n";
}
};
int main(int argc, char **argv)
{
std::vector <A> Avec;
std::cout << "resize start\n";
Avec.resize(1);
std::cout << "resize end\n";
return 0;
}
Outputs:
resize start
Calling default constructor 0x7fff9a34191f
Calling copy constructor 0x1569010 0x7fff9a34191f
Calling destructor 0x7fff9a34191f
resize end
It isn't performing two allocations, it is creating an object by the default constructor to pass into resize, then copying that object into the new position, then destructing the argument.
If you look at the arguments to resize:
void resize(n, t = T())
It has as a defaulted argument a default constructed object of type T (this is the default constructor being called in your output). Then, within the function, it copies this into the correct position (this is the copy constructor). After the resize function ends, destroys the argument (the destructor call in the output).
Here is a guess:
The compiler re-ordered the initial allocation of Avec until after the "resize start"
The vector is initially allocated with 0 elements
The resize gets the new element filled with a default A (which was achieved by createing a default A, copying it into the vector, and deleting the temporary.
If you initialize objects that way the vector template creates objects by making a copy.
If you don't want to call copy constructor you should make :
vector<A*> Avec;
avec.push_back(new A());
http://www.cplusplus.com/reference/stl/vector/vector/