¿Por qué la copia ctor utiliza en este código?
-
06-09-2019 - |
Pregunta
class A
{
public:
A(const int n_);
A(const A& that_);
A& operator=(const A& that_);
};
A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }
A::A(const A& that_) // This is line 21
{ cout << "A::A(const A&)" << endl; }
A& A::operator=(const A& that_)
{ cout << "A::operator=(const A&)" << endl; }
int foo(const A& a_)
{ return 20; }
int main()
{
A a(foo(A(10))); // This is line 38
return 0;
}
Al ejecutar este código da O / P:
A :: A (int), n_ = 10
A :: A (int), n_ = 20
Al parecer, el constructor de copia nunca es llamado.
class A
{
public:
A(const int n_);
A& operator=(const A& that_);
private:
A(const A& that_);
};
Sin embargo, si lo hacemos privada, se produce este error de compilación:
Test.cpp: En función de ‘int main ()’:
Test.cpp: 21: error: ‘A :: A (A & const)’ es privada
Test.cpp: 38: error: dentro de este contexto
¿Por qué se queja el compilador cuando no utiliza realmente el constructor de copia?
Estoy utilizando la versión de gcc 4.1.2 20070925 (Red Hat 4.1.2-33)
Solución
Core defecto 391 explica el problema.
Básicamente, el estándar de C ++ actual requiere un constructor de copia para estar disponible cuando se pasa un temporal del tipo de clase a una referencia const.
Este requisito se eliminará en C ++ 0x.
La lógica detrás de requerir un constructor de copia proviene de este caso:
C f();
const C& r = f(); // a copy is generated for r to refer to
Otros consejos
El estándar 2003, en §12.2 / 1, establece:
Aún cuando la creación de la objeto temporal se evita (12.8), todas las restricciones semánticas deben estar respetado como si el objeto temporal fue creado. [Ejemplo: incluso si la constructor de copia no se llama, todo las restricciones semánticas, tales como accesibilidad (cláusula 11), será satisfecho. ]
Hay ejemplos similares en todo. Por lo que sé, el compilador es libre para generar u optimizar los temporales a la basura.
Por lo que veo no está utilizando el constructor de copia en cualquier lugar. En el foo(A(10))
comunicado que está creando un objeto temporal de la clase A y que pasa como una constante referencia a foo. El foo devuelve un entero que se utiliza en la construcción de a
objeto. Por lo tanto, no veo donde el constructor de copia se está involucrando aquí y cómo NRVO entra en el cuadro. Además, he realizado el siguiente código al hacer que el constructor de copia privada y que compila bien en VS2008.
using namespace std;
class A
{
public:
A(const int n_);
private:
A(const A& that_);
A& operator=(const A& that_);
};
A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }
A::A(const A& that_) // This is line 21
{ cout << "A::A(const A&)" << endl; }
A& A::operator=(const A& that_)
{
cout << "A::operator=(const A&)" << endl;
return *this;
}
int foo(const A& a_)
{ return 20; }
int main(int argc,char *argv[])
{
A a(foo(A(10))); // This is line 38
return 0;
}
Sólo otra observación: el compilador hace una cosa diferente cuando se trabaja con un temporal. Por lo tanto, no se trata del constructor de copia, se trata de la intermedia temporal.
A original(10);
foo( original ); // does compile
foo( A(10) ); // doesn't compile - needs a copy constructor
En la expresión:
A a(foo(A(10)));
El resultado de la A(10)
sub-expresión es un rvalue de tipo A
. (5.2.3 [expr.type.conv])
Al inicializar una referencia constante de un rvalue El compilador puede crear un temporal de la rvalue y se unen a que la referencia. Incluso si elige no, el constructor copia debe ser accesible. (8.5.3 [decl.init.ref]) Este no sería el caso si no se está inicializando referencia de un de referencia compatibles lvalue , donde la unión directa tiene el mandato.
Como foo
toma su parámetro por referencia y no el valor, no hay ninguna copia por mandato de la propia inicialización argumento.
foo
devuelve un int, por lo que no hay ninguna copia de un A
aquí.
a
se inicia directamente desde el int devuelto por foo, lo que no hay copia de A
aquí.
El constructor de copia no se utiliza, pero para que el código para compilar el constructor de copia tienen que ser accesibles.
EDIT: Comeau C ++ informes compilador lo siguiente:
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions
"ComeauTest.c", line 38: error: "A::A(const A &)" (declared at line 17), required
for copy that was eliminated, is inaccessible
A a(foo(A(10))); // This is line 38
^
1 error detected in the compilation of "ComeauTest.c".
Tenga en cuenta que si están habilitadas las extensiones C ++ 0x, que compila bien en Comeau compilador de C ++.
En general, no se debe llegar a preocupado por si y cuando el constructor de copia se llama. El estándar de C ++ es bastante relajado acerca de cuándo se retirarán las llamadas al constructor de copia, o para el caso añadió. Si su clase lógicamente lo necesita, proporcionarla (y no se olvide el operador destructor y asignación) es la regla sensata.
Cuando se llama a:
foo( A(10) );
un objeto temporal se crea durante la vida de la llamada. Un constructor de copia se utiliza para rellenar los datos. El objeto temporal se retira después de la ejecución de la llamada.
Cuando se llama a:
{
A original(10);
foo( original );
}
El original está siendo desechado después de salir del bloque. Es de forma segura se puede utilizar como un parámetro.
Para la velocidad óptima, pasar el objeto por referencia, utilizando una variable temporal que será desechado por el compilador durante su optimización.