There is a code in Maine:

my_complex::complex cd = my_complex::complex::add_complex(ca, cb); 

The add_complex(ca, cb) function has the following prototype:

 static complex add_complex(const complex&, const complex&); 

Both assignment operators for the complex class are declared in the class and when assigned to the form

 ca = cb; //срабатывает копирующее присваивание ca = complex(5,8); //срабатывает перемещающее присваивание 

operators work. However, in the code presented at the top of the question (assignment of the function result), none of the operators and constructors are called. Is this behavior the result of optimizing a compiler that writes the result of a function directly into a receiver variable, or is this behavior due to something else?

G ++ compiler C ++ 14.

    2 answers 2

    An assignment statement applies to objects that have already been created. However, in this sentence

     my_complex::complex cd = my_complex::complex::add_complex(ca, cb); 

    only an object is created. Therefore, the constructor will be called here. In the general case, a copy constructor or a motion constructor is called here, depending on which of them is declared.

    The compiler is allowed to omit the call to the copy constructor or move constructor and create the object "in place" in the cd . However, however, a copy constructor or a motion constructor must be available.

    Therefore, you should also not include in the copy constructor or the displacement constructor any side effects that do not occur in ordinary constructors when creating objects.

    Here is a demo program.

     #include <iostream> class A { public: A( int x ) { std::cout << "A::A( int ) with x = " << x << std::endl; } A( A &&a ) { std::cout << "A::A( A && )" << std::endl; } A & operator =( A && ) { std::cout << "A::operator =" << std::endl; } }; int main() { // объект создается; вызывается конструктор A a = A( 10 ); // объект уже создан; вызывается оператор присваивания a = A( 20 ); return 0; } 

    Output of the program to the console

     A::A( int ) with x = 10 A::A( int ) with x = 20 A::operator = 

    In this program, the compiler skips the call to the move constructor. However, as I said above, it should be available. If, for example, it is closed, the code will not compile.

     #include <iostream> class A { private: A( A &&a ) { std::cout << "A::A( A && )" << std::endl; } public: A( int x ) { std::cout << "A::A( int ) with x = " << x << std::endl; } A & operator =( A && ) { std::cout << "A::operator =" << std::endl; } }; int main() { // объект создается; вызывается конструктор A a = A( 10 ); // объект уже создан; вызывается оператор присваивания a = A( 20 ); return 0; } prog.cpp: In function 'int main()': prog.cpp:6:5: error: 'A::A(A&&)' is private A( A &&a ) ^ prog.cpp:27:17: error: within this context A a = A( 10 ); ^ 

    Keep in mind that MS VC ++ (at least some of its versions) has a bug and allows you to compile this code. :)

    From the standard C ++ (12.8 Copying and moving class objects)

    If you’re looking for a class object , it’s even if you ’ve been using it . It has been noted that this is the case. This is a scam that has been taken to be the case:

      In code

       my_complex::complex cd = my_complex::complex::add_complex(ca, cb); 

      There is no assignment operator. This is not "assigning the result of a function," as you incorrectly called it. This is exactly where initialization is done. Moreover, due to the fact that the type of the left part coincides with the type of the right, such initialization by copying is regarded as direct initialization.

       my_complex::complex cd(my_complex::complex::add_complex(ca, cb)); 

      And then the compiler simply eliminated the intermediate temporary objects and constructs the result of the function directly in cd - copy elision and return value optimization work . Those. the constructor is called, but it is the one that is called inside add_complex (the contents of which you, however, do not show) and is called immediately for cd . Thus, copying / moving is not necessary.

      This kind of "reduction" of the initialization process and the elimination of "unnecessary" intermediate temporary objects and copying / moving is openly allowed by the language specification, even if copy / move constructors have side effects.