Copy Elision Misunderstanding - c++

#include <iostream>
struct A
{
A() { std::cout << "Def Constr\n"; }
A(const A&) { std::cout << "Copy Constr\n"; }
};
A func1()
{
return A{};
}
void func2(A a) {}
int main()
{
func2(func1());
}
After compiling with
g++ Copy.cpp -std=c++11 -fno-elide-constructors
Output is :
Def Constr
Copy Constr
Copy Constr
And my questions is : Why 2 Copy Constr ? I thought only 1 Copy was needed.
I might have a guess that func1() throws a temp object and this temp object needs to be copied to another memory region and from that region again a copy must be made for the func2() parameter but it's vague for me .
Could you explain it in detail please ?

The return value of func1 is copied from the expression A{}.
The value of the function call expression func1() is copied into the function parameter of func2.

Yes your understanding is right. Your line of code (without copy eliding) is similar to
int main()
{
{
A temp = func1(); // 2nd copy
func2(temp); // 3rd copy
}
}

Related

What is the best way to return multiple large objects in C++?

I want to return a tuple containing types like std::vector or std::unordered_map etc. where the objects may be large enough that I care about not copying. I wasn't sure how copy elision / return value optimization will work when the returned objects are wrapped in a tuple. To this end I wrote some test code below and am confused by parts of its output:
#include <tuple>
#include <iostream>
struct A {
A() {}
A(const A& a) {
std::cout << "copy constructor\n";
}
A(A&& a) noexcept {
std::cout << "move constructor\n";
}
~A() {
std::cout << "destructor\n";
}
};
struct B {
};
std::tuple<A, B> foo() {
A a;
B b;
return { a, b };
}
std::tuple<A, B> bar() {
A a;
B b;
return { std::move(a), std::move(b) };
}
std::tuple<A, B> quux() {
A a;
B b;
return std::move(std::tuple<A, B>{ std::move(a), std::move(b) });
}
std::tuple<A, B> mumble() {
A a;
B b;
return std::move(std::tuple<A, B>{ a, b });
}
int main()
{
std::cout << "calling foo...\n\n";
auto [a1, b1] = foo();
std::cout << "\n";
std::cout << "calling bar...\n\n";
auto [a2, b2] = bar();
std::cout << "\n";
std::cout << "calling quux...\n\n";
auto [a3, b3] = quux();
std::cout << "\n";
std::cout << "calling mumble...\n\n";
auto [a4, b4] = mumble();
std::cout << "\n";
std::cout << "cleaning up main()\n";
return 0;
}
when I run the above (on VS2019) I get the following output:
calling foo...
copy constructor
destructor
calling bar...
move constructor
destructor
calling quux...
move constructor
move constructor
destructor
destructor
calling mumble...
copy constructor
move constructor
destructor
destructor
cleaning up main()
destructor
destructor
destructor
destructor
So from the above is looks like bar() is best which is return { std::move(a), std::move(b) }. My main question is why foo() ends up copying? RVO should elide the tuple from being copied but shouldn't the compiler be smart enough to not copy the A struct? The tuple constructor could be a move constructor there since it is firing in an expression that is being returned from a function i.e. because struct a is about to not exist.
I also don't really understand what is going on with quux(). I didnt think that additional std::move() call was necessary but I don't understand why it ends up causing an additional move to actually occur i.e. I'd expect it to have the same output as bar().
My main question is why foo() ends up copying? RVO should elide the
tuple from being copied but shouldn't the compiler be smart enough to
not copy the A struct? The tuple constructor could be a move
constructor
No, move constructor could only construct it from another tuple<> object. {a,b} is constructing from the component types, so the A and B objects are copied.
what it going on with quux(). I didnt think that additional
std::move() call was necessary but I don't understand why it ends up
causing an additional move to actually occur i.e. I'd expect it to
have the same output as bar().
The 2nd move happens when you are moving the tuple. Moving it prevents the copy elision that occurs in bar(). It is well-know that std::move() around the entire return expression is harmful.

Copy elision with tuples

I always had the wrong impression that if I create temporaries in a function that returns a tuple, and use std::forward_as_tuple in the return statement, then there is no copy, just like automatic copy elision for non-tuple return types.
How then can we avoid a copy? For example, in the following code, func1 “copies” the values 1 and 2 into temp as it is being constructed, and there is no additional copy upon returning. func2 on the other hand does the same construction, but additionally copies when a tuple is created in the return statement.
I tried to be witty about it, and declare the temporary to be returned a tuple and construct a My_struct in place, like this: std::tuple<My_struct> temp {{1,2}}. But the copy constructor is still called. Adding a move constructor won't help as far as I can see since the data members are simple types.
#include <iostream>
#include <tuple>
struct My_struct {
int x, y;
My_struct(int x, int y) :
x(x), y(y)
{
std::cout << "Constructor called\n";
}
My_struct(const My_struct &a) :
x(a.x), y(a.y)
{
std::cout << "Copy constructor called!\n";
}
};
My_struct func1()
{
My_struct temp {1,2};
return temp;
}
std::tuple<My_struct> func2()
{
My_struct temp {1,2};
return std::forward_as_tuple(temp);
}
int main()
{
std::cout << "# Calling func1\n";
auto result1 = func1();
std::cout << "# Calling func2\n";
auto result2 = func2();
}
result:
# Calling func1
Constructor called
# Calling func2
Constructor called
Copy constructor called!

copy initialization : why move or copy constructor was not called even if copy-elision is turned off?

My question is different because I may "know" copy-elision. I am learning copy initialization. However,the following code confused me because I have already turned off the copy-elision using -fno-elide-contructors -O0 option.
#include <iostream>
using namespace std;
class test{
public :
test(int a_, int b_) : a{a_}, b{b_} {}
test(const test& other)
{
cout << "copy constructor" << endl;
}
test& operator=(const test& other)
{
cout << "copy assignment" << endl;
return *this;
}
test(test&& other)
{
cout << "move constructor" << endl;
}
test& operator=(test&& other)
{
cout <<"move assignment" << endl;
return *this;
}
private :
int a;
int b;
};
test show_elide_constructors()
{
return test{1,2};
}
int main()
{
cout << "**show elide constructors**" <<endl;
show_elide_constructors();
cout << "**what is this?**" <<endl;
test instance = {1,2};//why neither move constructor nor copy constructor is not called?
cout << "**show move assignment**" <<endl;
instance = {3,4};
return 0;
}
I first build with the command:
g++ -std=c++11 -fno-elide-constructors -O0 main.cpp -o main and I got the result as following:
**show elide constructors**
move constructor
**what is this?**
**show move assignment**
move assignment
Then I built with command without -fno-elide-constructor -O0 option to attest that g++ indeed turned off the optimization in my previous build.
So, why test instance = {1,2} does not call copy constructor or move constructor? Isn't a temp object created from the implicit conversion? And instance is supposed to be initialized by that temp object?
why test instance = {1,2} does not call copy constructor or move constructor?
It shouldn't. test instance = {1,2} is copy-list-initialization, as the effect, the appropriate constructor (i.e. test::test(int, int)) is used to construct the object instance directly. No needs to construct a temporary and call copy/move constructor here.
OK. I add some supplementary information here.
: Copy initialization, List initialization.
So T object = {other} is indeed copy initialization until c++11, which consider it as list initialization.
BTW, if the constructor is explicit, the call would fail.

Why this move constructor is so greedy?

I have the following code:
#include <iostream>
class foo_class {
std::string value;
public:
foo_class(const foo_class& v) : value{v.value} {
std::cout << "copy constructor" << std::endl;
}
foo_class(foo_class&& v) : value{std::move(v.value)} {
std::cout << "move constructor" << std::endl;
}
~foo_class() {
std::cout << "destructor" << std::endl;
}
foo_class(std::string v) : value{std::move(v)} {
std::cout << "type constructor" << std::endl;
}
};
struct Data {
foo_class a;
foo_class b;
};
int main() {
std::string c = "3";
Data x{c,c+"3"};
return 0;
}
IMPORTANT, I compile it with GCC and Clang (4.8.2 and 3.4 respectively) and the flag -fno-elide-constructors, so we don't elide the copy/move constructors.
The result of the execution is the following one:
type constructor
move constructor
destructor
type constructor
move constructor
destructor
destructor
destructor
Which means that the copy constructor is not being used at all, even when it should be used for the first argument of the constructor of the struct Data.
Next, If I delete the copy constructor, the code is still legal according to my compilers, but in my understanding should be illegal, cause I'm not casting to && (using std::move) when passing the first argument of the constructor of Data.
My question is simple:
Why is this happening?
Am I missing something?
Because the foo_class objects constructed in
Data x{c,c+"3"};
are both temporaries. So they invoke move constructor instead of copy constructor.
type constructor // 1. Construct a temporary foo_class object out of string "c" for a
move constructor // 2. Move the temporary object created above to x.a
destructor // 3. Destruct the temporary foo_class object created in 1
type constructor // 4. Same as 1, but the object is for b, and the string "33"
move constructor // 5. Same as 2, moved to x.b
destructor // 6. Destruct the temporary foo_class object created in 4
destructor // 7. Destruct x.b
destructor // 8. Destruct x.a

constructor and copy constructor behaviour in c++ [duplicate]

This question already has answers here:
Why copy constructor is not called here?
(2 answers)
Closed 8 years ago.
I have the following code:
#include <iostream>
using namespace std;
class X
{
public:
int g;
X() { cout << "constr" << endl; }
X(const X& ref1) { cout << "copy constr" << endl; }
};
X f()
{
X ee;
ee.g = 1;
return ee;
}
int main()
{
X ff = f();
return 0;
}
Running the code I see that the constructor was called only once and the copy constructor was never called. Don't you expect two constructor and one copy constructor calls here? Thanks!
This is a special case of the copy elision called return value optimization (the link explains precisely your case).
Copy Elision is an optimization implemented by many compilers to prevent extra, unnecessary, copies. Makes the return-by-value or pass-by-value possible in practice.
Take a look at the example in the following answer: https://stackoverflow.com/a/12953129/1938163
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C();
}
int main() {
std::cout << "Hello World!\n";
C obj = f();
}
(http://en.wikipedia.org/wiki/Return_value_optimization#Summary)
Perhaps incredible to believe the first time, depending on the compiler & settings, the following outputs are all valid:
Hello World!
A copy was made.
A copy was made.
Hello World!
A copy was made.
Hello World!
In your case, this is a special copy elision optimization called RVO - Return Value Optimization where an object returned by value from a method has its copy elided.