views:

197

answers:

2

Here is an extract from item 56 of the book "C++ Gotchas":

It's not uncommon to see a simple initialization of a Y object written any of three different ways, as if they were equivalent.

Y a( 1066 ); 
Y b = Y(1066);
Y c = 1066;

In point of fact, all three of these initializations will probably result in the same object code being generated, but they're not equivalent. The initialization of a is known as a direct initialization, and it does precisely what one might expect. The initialization is accomplished through a direct invocation of Y::Y(int).

The initializations of b and c are more complex. In fact, they're too complex. These are both copy initializations. In the case of the initialization of b, we're requesting the creation of an anonymous temporary of type Y, initialized with the value 1066. We then use this anonymous temporary as a parameter to the copy constructor for class Y to initialize b. Finally, we call the destructor for the anonymous temporary.

To test this, I did a simple class with a data member (program attached at the end) and the results were surprising. It seems that for the case of c, the object was constructed by the copy constructor rather than as suggested in the book.

Does anybody know if the language standard has changed or is this simply an optimisation feature of the compiler? I was using Visual Studio 2008.

Code sample:

#include <iostream>

class Widget
{
    std::string name;
public:
    // Constructor
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
    // Copy constructor
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
    // Assignment operator
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};

int main(void)
{
    // construct
    Widget a("a");
    // copy construct
    Widget b(a);
    // construct and assign
    Widget c("c"); 
    c = a;
    // copy construct!
    Widget d = a;
    // construct!
    Widget e = "e";
    // construct and assign
    Widget f = Widget("f");

    return 0;
}

Output:

Constructing Widget a

Copy constructing Widget from a

Constructing Widget c
Assigning Widget from a to c

Copy constructing Widget from a

Constructing Widget e

Constructing Widget f
Copy constructing Widget from f

I was most surprised by the results of constructing d and e. To be precise, I was expecting an empty object to be created, and then an object to be created and assigned to the empty object. In practice, the objects were created by the copy constructor.

+3  A: 

The compiler is permitted to optimize cases b and c to be the same as a. Further, copy construction and assignment operator calls can be wholly eliminated by the compiler anyway, so whatever you see isn't necessarily the same with different compilers or even compiler settings.

sbi
So this means the book may not be 100% correct now? Interesting.
Andy
@Andy, no the book is 100% correct. But it's not 100% complete. If you want 100% completion, you need to consult the Standard. Specifically, it (or the part you quoted) does not explain that the compiler is allowed to optimize copies if copying is done from a temporary like above.
Johannes Schaub - litb
Thank you. I guess you should still use the formats suggested by the book, in case that the compiler you are using does not implement the optimisation.
Andy
No you should not. Use whichever is clearer and don't worry about those minor optimizations.
Matthieu M.
@Johannes: "The book says the truth, and nothing but the truth, but not the whole truth." `:)`
sbi
+11  A: 

The syntax

X a = b;

where a and b are of type X has always meant copy construction. Whatever variants, such as:

X a = X();

are used, there is no assignment going on, and never has been. Construct and assign would be something like:

X a;
a = X();
anon
Thanks for the comment about copy construction. I was not really aware of that - I always thought that you have to do X=a(b); to be sure of invoking the copy constructor.
Andy