Determinar qué constructor de copia llamar en C ódigo C++

He tenido muchos problemas.

he escrito un simple ejemplo de clase C++ que contiene un constructor no paramétrico, un constructor paramétrico, dos constructores replicantes, un operador de asignación y un operador de signo más.
class Complex {
protected:
    float real, img;
public:
    Complex () : real(0), img(0) {
        cout << "Default constructor\n";
    }

    Complex (float a, float b) {
        cout << "Param constructor" << a << " " << b << endl;
        real = a;
        img = b;
    }

    // 2 copy constructors
    Complex( const Complex& other ) {
        cout << "1st copy constructor " << other.real << " " << other.img << endl;
        real = other.real;
        img = other.img;
    }

    Complex( Complex& other ) {
        cout << "2nd copy constructor " << other.real << " " << other.img << endl;
        real = other.real;
        img = other.img;
    }

    // assignment overloading operator
    void operator= (const Complex& other) {
        cout << "assignment operator " << other.real << " " << other.img << endl;
        real = other.real;
        img = other.img;
    }

    // plus overloading operator
    Complex operator+ (const Complex& other) {
        cout << "plus operator " << other.real << " " << other.img << endl;
        float a = real + other.real;
        float b = img + other.img;
        return Complex(a, b);
    }

    float getReal () {
        return real;
    }

    float getImg () {
        return img;
    }
};
Utilizo principalmente esta clase de esta manera:
int main() {
    Complex a(1,5);
    Complex b(5,7);
    Complex c = a+b; // Statement 1
    system("pause");
    return 0;
}
Los resultados se imprimen como sigue:
Param constructor 1 5
Param constructor 5 7
plus operator 5 7
Param constructor 6 12
Creo que el constructor de copia debe ser utilizado en la declaración 1, pero realmente no sé cuál es la llamada.
¿Por favor, dime cuál y por qué?
Muchas gracias

La mejor manera de manejarlo para mí

, el compilador está omitiendo la llamada al constructor de copia (en realidad dos llamadas). Esto está permitido (pero no obligatorio!) De acuerdo con el párrafo 12.8/31 de la norma C++ 11, incluso si el constructor o destructor tiene efectos secundarios:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. [..] This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

[...]

— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move


Si el compilador no omite la llamada al constructor de copia, el primero se selecciona dos veces, es decir, uno con la siguiente firma:
Complex( const Complex& other )
Las razones son:
El valor devuelto por
  • operator + se inicializa copiando del archivo temporal (Complex(a, b)) y sólo la referencia de valor izquierdo a const se puede enlazar al archivo temporal. Si operator + estuviera escrito de esta manera, la situación sería diferente:
    Complex operator+ (const Complex& other) {
        // ...
        Complex c(a, b);
        return c;
    } 
    
    En este caso, se invoca un segundo constructor de réplicas, ya que c no está calificado como const y es un valor izquierdo, por lo que puede estar vinculado a una referencia de valor izquierdo no const;
  • El objeto
  • c en main() es copiado de un valor derecho (el valor devuelto por operator + también es temporal). No importa cómo operator + devuelve su objeto Complex, siempre y cuando vuelva por valor. Por lo tanto, el primer constructor de copia se selecciona siempre y cuando no se realice la omisión de copia.
  • Si está usando GCC y desea verificar este comportamiento, intente establecer la bandera de compilación -fno-elide-constructors (también soportada por clang, pero hay un error en la versión 3.2, no sé si se ha arreglado).