2009-10-23 21 views
8

Aquí está un ejemplo de código mínima que ilustra el problema:¿Por qué esta advertencia del compilador IBM XL C/C++?

#include <iostream> 

class Thing 
{ 
    // Non-copyable 
    Thing(const Thing&); 
    Thing& operator=(const Thing&); 

    int n_; 

public: 
    Thing(int n) : n_(n) {} 

    int getValue() const { return n_;} 
}; 

void show(const Thing& t) 
{ 
    std::cout << t.getValue() << std::endl; 
} 

int main() 
{ 
    show(3); 
} 

Esto produce el mismo error:

int main() 
{ 
    show(Thing(3)); 
} 

IBM XL C/C++ 8,0 compilador bajo AIX emite estas advertencias:

"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed. 
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed. 
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible. 

También probé g ++ 4.1.2 con "-Wall" y "-pedantic" y no obtuve ningún diagnóstico. ¿Por qué se requiere el acceso al constructor de copia aquí? ¿Cómo puedo eliminar la advertencia, además de hacer que el objeto se pueda copiar (que está fuera de mi control) o hacer una copia explícita para pasar (cuando el objeto de la vida real es caro de copiar)?

+0

¿Está realmente usando ese constructor de copia dentro de la implementación de la clase en alguna parte? –

+0

No. He publicado todo el archivo de código que produjo los diagnósticos. –

+0

FYI: También probé con VC++ 2005 y 2008, compila sin advertencias. Entonces parece que tienes razón, el problema parece ser más bien el compilador de IBM específico – jdehaan

Respuesta

9

Las reglas para esto están en §8.5.3/5 de la norma. Hay tres situaciones básicas identificadas. La primera implica que el inicializador ('3' en su caso) sea un valor l o tenga un tipo de clase. Como ninguno de los dos es cierto, lo que tenemos es el tercer caso: inicializar una referencia constante con un valor r que no tiene un tipo de clase. Este caso está cubierto por la bala final en 8.5.3/5:

Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy initialization (8.5). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 must be the same cv-qualification as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed.

Editar: relectura, creo que IBM tiene razón. Anteriormente estaba pensando en la posibilidad de tener que copiar el temporal, pero esa no es la fuente del problema. Para crear la inicialización de copia temporal sin referencia como se especifica en §8.5, necesita el copiador. En particular, en este punto es equivalente a una expresión como:

T x = a;

Esto es básicamente equivalente a:

T x = T (a);

I.e. se requiere crear un temporal, luego copiar el temporal al objeto que se está inicializando (que, en este caso, es también un temporal). Para resumir el proceso requerido, que es más o menos equivalente a un código como:

T temp1(3); 
T temp2(temp1); // requires copy ctor 
show(temp2); // show's reference parameter binds directly to temp2 
+0

Buena información. Gracias por la investigación, Jerry. –

+0

Eso es realmente extraño, pero estoy de acuerdo con la lectura de tu/IBM del estándar. –

+0

se requiere crear un temporal. Técnicamente: es necesario __podría__ crear un temporal. Técnicamente, el compilador puede optimizar esto. –

3

C++ permite compiladores suficientemente inteligentes para evitar la copia de objetos temporales, la única infracción de la regla as-if permitida por la norma. No estoy familiarizado con el compilador AIX C++ de IBM, pero parece que cree que la llamada show(3) requiere la copia temporal de una cosa. En ese caso, C++ requiere que tenga un constructor de copia accesible aunque su compilador sea lo suficientemente inteligente como para evitar su uso.

¿Pero por qué el show(3) requiere una copia en primer lugar? Eso no lo puedo entender. Con suerte, litb estará en un tiempo.

+0

Eso es más o menos lo que estaba pensando. –

+0

"¿Pero por qué show (3) requiere una copia en primer lugar?" No creo que debería. El código debe crear un objeto temporal y vincularlo directamente al parámetro de función 'const Thing &'. Así que creo que Jerry tiene razón: esto es un error. – sbi

+0

He cambiado de opinión: después de volver a leer el estándar, estoy bastante seguro de que no es un error. –

0

¿Qué ocurre si intentas nombrar la Cosa temporal?

Thing temp(3);
show(temp);

+0

Eso elimina los mensajes. Probablemente haremos básicamente esto, pero probablemente con una asignación de montón. No es tan rápido, pero el objeto de la vida real es bastante grande para poner en la pila. Así que tal vez es mejor no confiar en el temporal de todos modos. –

1

Mi sensación es que Jerry answer es correcta, pero hay algunas preguntas todavía.

Lo que es interesante es que hay un problema central que cubre el párrafo anterior de esa sección (391).Esa cuestión se relaciona con cuando el argumento es del mismo tipo de clase. Específicamente:

int main() { 
    show (Thing (3));  // not allowed under current wording 
          // but allowed with Core Issue 391 

    show (3);    // Still illegal with 391 
} 

El cambio en el Tema Central 391 sólo afecta en el que el valor de lado derecho temporal tiene el mismo tipo de clase. La redacción anterior tenía:

If the initializer expression is an rvalue, with T2 a class type, and cv1 T1 is reference-compatible with cv2 T2, the reference is bound as follows:

[...]

The constructor that would be used to make the copy shall be callable whether or not the copy is actually done.

Esa última línea es lo que haría que show(Thing(3)) ilegal según el estándar actual. La redacción propuesta para esta sección es:

If the initializer expression is an rvalue, with T2 a class type, and "cv1 T1" is reference-compatible with "cv2 T2", the reference is bound to the object represented by the rvalue (see 3.10 [basic.lval]) or to a sub-object within that object.

En este punto, he considerado que g ++ pueden haber actualizado su comportamiento como por 391 pero que el cambio incluido accidentalmente el caso de copia de inicialización. Sin embargo, eso no se demuestra por las versiones de g ++ que he probado con:

class A{ 
public: 
    A(); 
    A (int); 
private: 
    A (A const &); 
}; 

void foo (A const &); 

void foo() 
{ 
    A a = 3 ;  // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR) 

    foo (3) ; // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK) 
    foo (A()); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK) 
    foo (A(3)); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK) 
} 

No puedo encontrar ningún fallo en la interpretación de Jerry para el caso foo (3), sin embargo, tengo dudas debido a la discrepancia entre los diferentes comportamientos del compilador .

Cuestiones relacionadas