2009-05-28 21 views
10
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; 
} 

ejecución de este código da O/P:¿Por qué se utiliza el copiador en este código?

A::A(int), n_=10
A::A(int), n_=20

Al parecer, el constructor de copia no se llama.

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: In function ‘int main()’:
Test.cpp:21: error: ‘A::A(const A&)’ is private
Test.cpp:38: error: within this context

Por qué se queja el compilador cuando no utiliza realmente el constructor de copia?
estoy usando la versión de gcc 4.1.2 20070925 (Red Hat 4.1.2-33)

Respuesta

12

Core defect 391 explican el problema.

Básicamente, el estándar actual de C++ requiere que un constructor de copia esté disponible al pasar un tipo temporal de clase a una referencia constante.

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 
+0

FYI Creo que esto todavía está presente en C++ 11 ('12.2/1'). –

1

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 compilador de C++ informa de 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 las extensiones C++ 0x están habilitados, que compila bien en Comeau compilador de C++.

+0

¿Qué diablos necesita Comeau para copiar el objeto A temporal? ¡Se pasa por referencia! – xtofl

+0

¿Y a qué optimización del valor de retorno se refiere? – xtofl

+1

No Se devuelve un objeto, por lo que RVO es irrelevante para el uso del constructor de copia. Sin embargo, A (10) es un valor r de tipo A, y el compilador puede elegir tomar una copia de esto para vincular el parámetro de referencia. Es esta copia potencial que requiere que los constructores de copia sean accesibles. –

3

Hasta donde veo, no está utilizando el constructor de copia en ningún lado. En la declaración foo(A(10)) está creando un objeto temporal de clase A y lo pasa como una referencia constante a foo. El foo devuelve un número entero que se usa en la construcción del objeto a. Por lo tanto, no veo dónde se está involucrando el constructor de copias aquí y cómo NRVO entra en escena. Además, compilé el siguiente código haciendo que el constructor de copias sea privado y se compiló 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; 

} 
+0

Lo mismo en VS2005, por cierto. – xtofl

+1

@AngryWhenHungry: Por cierto, le faltaba devolver * esto en el operador de asignación. Espero que ese no sea el error del compilador que recibiste :-) – Naveen

+1

Mira la respuesta de James Hopkin: http://stackoverflow.com/questions/919701/c-why-is-the-copy-ctor-used-in-this -code/920118 # 920118. Básicamente, vincular un temporal a una referencia constante * todavía * requiere el copiador (en C++ 03). –

0

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 sobre cuándo se eliminarán las llamadas al constructor de copia, o para ese asunto agregado. Si su clase lo necesita lógicamente, proporcionelo (y no olvide el destructor y el operador de asignación) es la regla sensata.

5

El estándar 2003, en §12.2/1, los Estados:

Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11), shall be satisfied. ]

Hay ejemplos similares alrededor. Por lo que sé, el compilador es libre de generar temporales o optimizarlos.

+0

¿Pero cómo la semántica de foo (const A &) dicta el uso de un constructor de copia? – xtofl

2

Otra observación más: el compilador hace algo diferente cuando trabaja con un temporal. Por lo tanto, no se trata del constructor de copias, sino del intermedio temporal.

A original(10); 
foo(original); // does compile 
foo(A(10)); // doesn't compile - needs a copy constructor 
0

Al llamar:

foo(A(10)); 

un objeto temporal se crea durante la vida de la llamada. Un constructor de copia se está utilizando para poblar los datos. El objeto temporal se elimina después de la ejecución de la llamada.

Al llamar:

{ 
    A original(10); 
    foo(original); 
} 

El original está siendo descartados después de salir del bloque. Se puede usar de forma segura como parámetro.

Para una velocidad óptima, pase el objeto por referencia, utilizando una variable temporal que será descartada por el compilador durante su optimización.

1

En la expresión:

A a(foo(A(10))); 

El resultado de la sub-expresión A(10) es un rvalue de tipo A. (5.2.3 [expr.type.conv])

Al inicializar una referencia const de un rvalue el compilador puede crear un temporal de la rvalue y obligar que a la referencia. Incluso si elige no hacerlo, se debe poder acceder al constructor de copias. (8.5.3 [decl.init.ref]) Este no sería el caso si se estuviera inicializando una referencia de un compatible con la referencialvalue donde el enlace directo es obligatorio.

Como foo toma su parámetro por referencia y no por valor, no hay una copia obligatoria para la inicialización del argumento.

foo devuelve un int, por lo que no hay copia de A aquí.

a se inicializa directamente desde el int devuelto por foo, por lo que no hay copia de A aquí.

Cuestiones relacionadas