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)

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top