0

I am studying this little example below just to understand better the behavior of passing values by copy in C++.

#include <iostream>
using namespace std;

class A{
public:
    A(){
        cout << "A()" << endl;
    }
    A(const A& a){
        cout << "A(const A& a)" << endl;
    }
    A(A&& a){
        cout << "A(A&& a)" << endl;
    }
};

A get_A(A a){
    cout << "beginning get_A()" << endl;
    A a2;
    cout << "end get_A()" << endl;
    return a2;
}

int main() {
    cout << "beginning main" << endl;
    A a1;
    A a2 = get_A(a1);
    cout << "end main" << endl;
    return 0;
}

Output:

beginning main
A()
A(const A& a)
beginning get_A()
A()
end get_A()
end main

Why after the printed end get_A() there isn't a A(const A& a), since I am returning by copy?

LunaticSoul
  • 599
  • 1
  • 4
  • 10
  • `A(A&& a)` is actually a move constructor, receiving a rvalue reference, though it doesn't play any role in this example. – LunaticSoul May 07 '15 at 04:07
  • @ArunA.S "AND"? That´s a rvalue reference. – deviantfan May 07 '15 at 04:07
  • 1
    Seems like you're compiling in a `release` mode with optimization turned on. So you ara running into (N)RVO optimization which eliminates redundant copy whenever it can. To understand anything better do not use `release` modes since it always use some "clever tricks" which mihgt hinder the learing process. – ixSci May 07 '15 at 04:10
  • Sorry, my compiler complained about this, so I thought it was AND. Is that part of C++14? – Arun A S May 07 '15 at 04:11
  • 1
    It's the move constructor, and not the copy constructor, that would've been called had it not been for copy elision/return value optimization. If you're using gcc or clang add `-fno-elide-constructors` to the compiler flags and see the difference in behavior. – Praetorian May 07 '15 at 04:12
  • @Praetorian nice tip, now two move constructors operation are being printed, I can now investigate further the behavior. Thanks. – LunaticSoul May 07 '15 at 04:19
  • 1
    @LunaticSoul That's probably the `a2` within `get_A()` being moved into the return value of the function, and then that return value being move constructed into `a2` in `main()`. Add `cout << this << endl;` within each of your constructors and the destructor definition (create one) to trace what objects are being created and destroyed. – Praetorian May 07 '15 at 04:23
  • What's the point on `cout << this << endl;`? I see that it prints the memory address where the `this` pointer of the object is stored, but what useful information can I extract from it? – LunaticSoul May 07 '15 at 14:01

1 Answers1

1

This is an optimization known as copy elision. When you call get_a, the compiler is constructing the local a2 variable in the place of a2 in main, and omitting the copy/move.

This is one of the few optimizations that the compiler is explicitly allowed to perform by the standard. The two situations it happens in are when you return a variable with automatic storage duration, or when you return a nameless temporary; though there are some additional restrictions.

Collin Dauphinee
  • 13,664
  • 1
  • 40
  • 71