I have this code

#include <iostream> using namespace std; class Unit{ int a_; char* pch_; public: Unit(){ cout << "Simple constr" << endl;} Unit(int r) : a_(r), pch_(new char[100]){ cout << "Constr " << endl;} Unit(const Unit& ){ cout << "Constr copy " << endl;} ~Unit(){ delete [] pch_; cout << "Destr" << endl; } }; int main(){ Unit a = 20; return 0; } 

Only the constructor with the parameter is invoked and the destructor is natural.

Why not so: 1. The designer with parameter is called: Unit (20). 2. There is an assignment to an object that was not created - this is a copy. Called copy constructor. Just the rvalue can be passed by const T &. 3. Destructor for rvalue. 4. Destructor for a.

    3 answers 3

    In "classic" C ++ (C ++ 98), your initialization is copy-initialization

     Unit a = 20; 

    really conceptually meant exactly

     Unit a = Unit(20); 

    using the Unit::Unit(int) conversion constructor and then the Unit::Unit(const Unit &) copy constructor Unit::Unit(const Unit &) . However, even in the "classic" C ++ compiler it was allowed to exclude the formation of an intermediate temporary object and optimize the code before direct initialization (direct-initialization)

     Unit a(20); 

    those. to exclude the call of the copy constructor even if the copy constructor had side effects. This optimization is called copy elision . (Even when copy elision was running, an accessible copy constructor was still required.)

    Beginning with C ++ 17, a guaranteed copy elision appeared in the language, with which your initialization is treated as

     Unit a(20); 

    without the requirement of having a copy constructor.

    Those. in any case, you should not expect a mandatory call to the copy constructor here (and should never have been). In the "classic" C ++, the copy constructor could have been called here, but no more.


    In your particular case, initialization by copying (copy-initialization) behaves identically to direct initialization (direct-initialization), but in general, significant differences between these forms of initialization are also preserved in C ++ 17. for example

     struct A { A(int) {} }; struct B { operator int() const { return 0; } }; int main() { B b; A a1(b); // Все в порядке A a2 = b; // Ошибка } 

    PS There is no “assignment” here, of course, there is no trace.

    • Understood, thanks a lot! - Semerkin

    Let presence = not be misleading in you, the form of the record Unit a = 20; copy initialization is called it is completely analogous to the record Unit a(20); , with the exception of not allowing the explicit constructor to be called.

    Starting with C ++ 11, use list initialization Unit a{20}; .

    Starting from C ++ 17, it is no longer possible to call the copy / move constructor when creating a temporary object, i.e.

     Unit foo(void) { return Unit{Unit{20}}; } Unit a{Unit{foo()}}; 

    Will cause only one constructor to be called.

    • I understand that is similar. It is just a matter of implicit type casting - we get the Unit object from int. How can we get this object without calling the constructor? - Semerkin
    • @Semerkin Write Unit a = 20; calls the constructor. If you declare the Unit(int r) constructor Unit(int r) as explicit , such an entry will cause an error. - VTT
    • I know about the use of explicit - forbids casting, in the mechanism of which I want to understand (in this context) - Semerkin
    • @VTT, but it is possible a piece of the standard (or other material) that starting from c ++ 17 it is impossible to call the copy constructor when assigning a temporary object - xperious
    • In the general case, the statement about "completely analogous" is completely wrong. Even in the presence of guaranteed copy elision, the difference between copy-initialization and direct-initialization persists in C ++ 17 - AnT

    Because

     Unit a = 20; 

    it is essentially the same as

     Unit a(20); 

    just another form of recording. Those. Unit(int r) constructor is called Unit(int r) .

    And when you exit the main - destructor.

    • This is the same, I agree. But this is achieved by implicit type casting. Ie from an object of type int, we should get an object of type Unit. And only then make a copy. - Semerkin
    • one
      From an object of type int, it turns out an object of type Unit. Named a . - Harry