4

Here is a simple class header file and a main program. In the main program, I thought that the copy constructor is called in exactly three situations: initialization(explicit copy), pass by value for function arguments, and return by value for functions. However it seems like it is not being called for one of them, I think either (3) or (4) as numbered in the comments. For which numbers (1) - (4) does it get called? Thanks.

X.h:

#include <iostream>

class X
{
public:
    X() {std::cout << "default constructor \n";}
    X(const X& x) { std::cout << "copy constructor \n";}
};

Main:

#include "X.h"

X returnX(X b)  // (1) pass by value - call copy constructor?
{
    X c = b;  // (2) explicit copy - call copy constructor?
    return b;  // (3) return by value - call copy constructor?
}

int main()
{
    X a; // calls default constructor

    std::cout << "calling returnX \n\n";
    X d = returnX(a);  // (4) explicit copy - call copy constructor?
    std::cout << "back in main \n";
}

Output:

default constructor
calling returnX

copy constructor
copy constructor
copy constructor 
back in main
1
  • 4
    The compiler is allowed to optimize it away. Commented Dec 26, 2013 at 16:25

2 Answers 2

7

Since copying happens frequently in C++ and since it may be expensive the compiler is allowed to elide certain copy (and move) constructions. This copy elision is allowed even if the elided constructor and/or the destructor has side effects like the output in your program (that is, it isn't really an optimization as the behavior with and without copy elision is different).

There are four basic places where copy elision can be applied according to 12.8 [class.copy] paragraph 31:

  1. In a return statement when directly returning a local variable which has the same type as the return type of the function.
  2. In a throw statement a copy of an automatic variable within the innermost try-block can be elided when the object is thrown.
  3. When a temporary object wasn't bound to a reference copying it can be elided.
  4. When a catch clause catches the object by value and with the same type as the object in the throw statement the copy can be elided.

The exact rules are slightly more complicated but I think this is the gist of it. Given that the rules for copy elision are fairly strict it is easy to suppress copy elision: the easiest way is to use an identity() function:

template <typename T>
T const& identity(T const& object) {
    return object;
}
...
X d = identity(returnX(a));

(this version also inhibits move construction, though; deducing the type using a T&& and returning it appropriately should make move construction possible but I'm not quite sure what the return type and the return statement should be).

Sign up to request clarification or add additional context in comments.

1 Comment

Warning: this answer predates C++17, in which the rules are different (though the results may be the same).
6

Number 4 is copying a temporary. It is a candidate for copy elision. That is, under certain conditions, the compiler is allowed to eliminate calls to the copy constructor, even if the copy constructor has side effects.

4 Comments

Actually both 3 and 4 are candidates for copy elision. The output might have contained just 2 copy constructions
@DavidRodríguez-dribeas: I thought NRVO was forbidden for parameters, no?
@DavidRodríguez-dribeas: copy-elision isn't permitted for parameters. Only local automatic variables, excluding parameters, are eligible for copy-elision. However, parameters are allowed to be moved (the next paragraph).
@DietmarKühl (and Benjamin): My bad, I had read the code as return c;, not return b;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.