0
#include <iostream>

class Test {
public:
    Test(const int& i) 
    {
        std::cout << "Direct" << std::endl;
    }
    Test(const Test& t) 
    {
        std::cout << "Copy" << std::endl;
    }
};

int main()
{
    Test test = 1;
    return 0;
}

This program(compiled in C++11) will only output Direct, but the Test test = 1; means implicit convert 1 to a Test and then copy the result to test, I expected it output both of Direct and Copy, could anyone explain it?

  • 4
    The search term you're looking for is "copy elision". It's legal, and in some cases required, that the copy constructor (and corresponding destructor) is not called when it appears that it ought to be. See for example [copy elision](https://en.cppreference.com/w/cpp/language/copy_elision) on cppreference.com – Arthur Tacca Sep 29 '20 at 16:32
  • So the purpose of this behavior is only for performance? And I have to do same thing in any constructor? –  Sep 29 '20 at 16:36
  • *"only for performance"* - I mean, yeah?! This is C++, where we strive to get the best performance possible. And even conceptually, what use is there for a temporary object that gets created only to initialize *another* object, and then immediately get destroyed afterwards?! Might as well collapse the entire initialization to a single c'tor invocation. – StoryTeller - Unslander Monica Sep 29 '20 at 16:38
  • 1
    Yes copy elision is a performance optimisation. I don't understand your question "I have to do the same thing...?" - you don't need to do anything, the compiler will do the optimisation. – Arthur Tacca Sep 29 '20 at 16:38
  • See also: [What are copy elision and return value optimization?](https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) – Arthur Tacca Sep 29 '20 at 16:43

1 Answers1

1

Until c++17, this initialization:

Test test = 1;

will create a temporary Test from the int 1, and then the copy constructor is invoked for test. In practice, compilers will do copy-elision, and the temporary will be elided. You can force the compiler to not do the elision by passing the -fno-elide-constructors flag, to see both constructor calls.

From c++17, the wording is changed, and there is simply no temporary on the right hand side, so there is nothing to elide, and only one constructor is called. So even if you use -fno-elide-constructors, you will only see a single constructor call.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • 2
    I thought `Test test = 1;` would directly construct even without copy elision since C++11! Am I misremembering? – Mooing Duck Sep 29 '20 at 16:41
  • 1
    @MooingDuck I think it was true even before C++11. This is just [copy initialization](https://en.cppreference.com/w/cpp/language/copy_initialization). – Arthur Tacca Sep 29 '20 at 16:46
  • 1
    @MooingDuck - You are. Try it with a deleted/inaccessible copy c'tor in C++11, and you should get an error. This is indicative of the whole thing being an optimization (as opposed to just the behavior that must happen). – StoryTeller - Unslander Monica Sep 29 '20 at 16:47
  • C++11 §12.6.1 specifically calls out `complex f = 3;` as an initializer, which is what confused me, but you're right that it clarifies: `construct complex(3) using complex(double) copy/move it into f`. §8.5.14 likewise says " The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the entity being initialized has a class type". §8.5.15 agrees "...is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). — end note ]" – Mooing Duck Sep 29 '20 at 17:21