1

Code :

#include <iostream>

class A {
public:
    A() {
    }

    A(const A& a) {
        std::cout << "Copy constructor" << std::endl;
    }

    A(A&& a) {
        std::cout << "Move constructor" << std::endl;
    }
};

int main()
{
    {//case 1
        A b = A(A());
    }
    std::cout << std::endl;
    {// case 2
        A a;
        A b = A(a);
    }
    std::cout << std::endl;
    {//case 3
        A a;
        A b = A(std::move(a));
    }
}

Output (with -O3 compilation flag) :

#case 1
#nothing printed

#case 2
Copy constructor

#case 3
Move constructor

In case 2, why is the copy constructor called even with maximum optimization level (-O3) ? I was expecting the compiler to detect that the variable 'a' is like being temporary (because used only for the construction of 'b') and to rather use the move constructor (like in case 3).

To my knowledge there is at least one case (return value optimization) where the compiler can alter the observable behavior of the program by avoiding the call to a copy constructor which has side effect.
So I was wondering if it could be possible in case 2, also for optimization purpose, to replace the call of the copy constructor by the move constructor, knowing that variable a is never used outside of the construction of b.

Ismael EL ATIFI
  • 1,939
  • 20
  • 16
  • 3
    In case 2, you create two named objects, so of course there have to be at least two constructor calls. `a` is not "like being a temporary" - its constructors have side effects. Constructors with side effects may be elided only in specific circumstances explicitly prescribed by the standard, not merely as a matter of optimization (optimizer must never change the observable behavior of a program). – Igor Tandetnik Oct 31 '18 at 04:10
  • Thanks @IgorTandetnik for your answer. So now same question but without the side effect in the default constructor (I updated code and output). – Ismael EL ATIFI Oct 31 '18 at 13:15
  • I mean : why is the compiler not treating 'a' as a temporary object in order to construct 'b' with its move constructor rather than its copy constructor ? – Ismael EL ATIFI Oct 31 '18 at 13:24
  • 2
    The compiler is not treating `a` as a temporary because it's not a temporary. Formally, it's an lvalue, not an rvalue. The standard prescribes that a copy constructor be used; calling a move constructor instead would alter the observable behavior of the program (a different line of text would be output). – Igor Tandetnik Oct 31 '18 at 13:53

2 Answers2

1

To my knowledge there is at least one case (return value optimization) where the compiler can alter the observable behavior of the program by avoiding the call to a copy constructor which has side effect.

These cases are explicitly specified by the standard, and unfortunately, cases like your case 2 are not included, so the compiler is not permitted to perform such an optimization that alters the observable behavior.


Related part in the standard [class.copy.elision]/3:

In the following copy-initialization contexts, a move operation might be used instead of a copy operation:

  • If the expression in a return statement ([stmt.return]) is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or

  • if the operand of a throw-expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one),

xskxzr
  • 12,442
  • 12
  • 37
  • 77
  • One of the reasons that your case 2 is not included is that, I guess, the compiler must **look ahead** to ensure `a` will not be used in the future, which may overload the compiler. – xskxzr Nov 02 '18 at 08:07
0

Although it is possible to get a temporary lvalue without a name (see this question), that is not what is happening here.

In case 2, a is not a temporary but a named lvalue, so no elison happens and copy constructor is called.

P.W
  • 26,289
  • 6
  • 39
  • 76