Pregunta

Aquí hay un ejemplo de código mínimo que ilustra el problema:

#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) );
}

El compilador IBM XL C / C ++ 8.0 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 tengo diagnóstico. ¿Por qué se requiere 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)?

¿Fue útil?

Solución

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

De lo contrario, se creará e inicializará un temporal del tipo "cv1 T1" a partir de la expresión de inicialización utilizando las reglas para una inicialización de copia no de referencia (8.5). La referencia se vincula entonces a lo temporal. Si T1 está relacionado con la referencia a T2, cv1 debe tener la misma calificación de cv o una mayor calificación de cv que cv2; De lo contrario, el programa está mal formado.

Editar: releyendo, creo que IBM lo tiene bien. Antes estaba pensando en la posibilidad de tener que copiar lo temporal, pero esa no es la fuente del problema. Para crear el temporal utilizando la inicialización de copia no de referencia como se especifica en §8.5, necesita el ctor de copia. 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, es aproximadamente 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

Otros consejos

C ++ permite que los compiladores suficientemente inteligentes eviten copiar objetos temporales, la única violación de la regla como-si permitida por la norma. No estoy familiarizado con el compilador AIX C ++ de IBM, pero parece que piensa que la llamada show (3) requiere una Cosa temporal para ser copiada. En ese caso, C ++ requiere que tengas un constructor de copias accesible, aunque tu compilador sea lo suficientemente inteligente como para evitar su uso.

Pero, ¿por qué show (3) requiere una copia en primer lugar? Que no puedo entender. Con suerte, litb llegará en breve.

Mi intuición es que Jerry's responde es correcto, pero aún quedan algunas preguntas.

Lo que es interesante es que hay un problema central que cubre el párrafo anterior de esa sección ( 391 ). Ese problema se relaciona con cuando el argumento es el 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 problema principal 391 afecta solo donde el valor temporal tiene el mismo tipo de clase. La redacción anterior tenía:

  

Si la expresión de inicialización es un rvalor, con T2 un tipo de clase y cv1 T1 es compatible con la referencia con cv2 T2, la referencia se enlaza de la siguiente manera:

     

[...]

     

El constructor que se usaría para hacer la copia será llamable, ya sea que la copia esté o no hecha realmente.

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

  

Si la expresión inicializadora es un rvalor, con T2 un tipo de clase y " cv1 T1 " es compatible con la referencia "cv2 T2", la referencia está vinculada al objeto representado por el valor r (ver 3.10 [basic.lval]) o a un subobjeto dentro de ese objeto.

En este punto, consideré que g ++ puede haber actualizado su comportamiento según 391 pero que el cambio incluyó accidentalmente el caso de inicialización de la copia. Sin embargo, eso no se demuestra en las versiones de g ++ que probé 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 fallas 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.

¿Qué sucede si intentas nombrar la cosa temporal?

Thing temp (3);
show(temp);

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