instalar - visual studio c++
objetos no copiables e inicialización de valores: g++ vs msvc (4)
Estoy viendo un comportamiento diferente entre g ++ y msvc alrededor del valor inicializando objetos no copiables. Considere una clase que no se puede copiar:
class noncopyable_base
{
public:
noncopyable_base() {}
private:
noncopyable_base(const noncopyable_base &);
noncopyable_base &operator=(const noncopyable_base &);
};
class noncopyable : private noncopyable_base
{
public:
noncopyable() : x_(0) {}
noncopyable(int x) : x_(x) {}
private:
int x_;
};
y una plantilla que usa la inicialización de valor para que el valor obtenga un valor conocido incluso cuando el tipo es POD:
template <class T>
void doit()
{
T t = T();
...
}
y tratando de usarlos juntos:
doit<noncopyable>();
Esto funciona bien en msvc a partir de VC ++ 9.0 pero falla en todas las versiones de g ++ con lo que probé esto (incluida la versión 4.5.0) porque el constructor de copia es privado.
Dos preguntas:
- ¿Qué comportamiento cumple con los estándares?
- Cualquier sugerencia de cómo solucionar esto en gcc (y para ser claros, cambiar eso a
T t;
no es una solución aceptable ya que esto rompe los tipos de POD).
PD: veo el mismo problema con boost :: noncopyable.
Hay §12.8 / 14:
Un programa está mal formado si el constructor de copia o el operador de asignación de copia para un objeto se usa implícitamente y no se puede acceder a la función de miembro especial.
Y luego está §12.8 / 15:
Cuando se cumplen ciertos criterios, una implementación puede omitir la construcción de copia de un objeto de clase, incluso si el constructor de copia y / o el destructor del objeto tienen efectos secundarios.
Entonces, la pregunta es realmente, si la implementación omite la llamada al constructor de copia (que está claramente permitido hacer), ¿el constructor de copia realmente se usa ?
Y, la respuesta a eso es sí, por §3.2 / 2:
Se usa un constructor de copia incluso si la implementación realmente elimina la llamada.
¿Has visto lo que sucede cuando compilas usando / Wall con MSVC? Establece lo siguiente acerca de su clase:
nocopy.cc(21) : warning C4625: ''noncopyable'' : copy constructor could not be
generated because a base class copy constructor is inaccessible
nocopy.cc(21) : warning C4626: ''noncopyable'' : assignment operator could not be
generated because a base class assignment operator is inaccessible
noncopyable
GCC: crear un constructor de copia para noncopyable
(¡y un operador de asignación idealmente!) Que hace lo posible por copiar la información de noncopyable_base
, es decir, invocando el constructor para noncopyable_base
que no tiene parámetros (ya que es el único accesible por noncopyable
) y luego copiando cualquier información de noncopyable_base
. Sin embargo, dada la definición de noncopyable_base
, parece que no hay datos para copiar, por lo que la simple adición de noncopyable_base()
a la lista de inicializadores de una nueva función noncopyable(const noncopyable &)
debería funcionar.
Sin embargo, tome nota de lo que MSVC dijo acerca de su programa. También tenga en cuenta que si utiliza T t()
lugar de T t = T()
, MSVC genera otra advertencia (C4930) , aunque GCC la acepta sin problemas.
No creo que se necesite metaprogramación de plantillas. Tratar
template <class T>
void doit()
{
struct initer { T t; initer() : t() {} } inited;
T& t = inited.t;
...
}
El comportamiento que está viendo en MSVC es una extensión, aunque está documentado como tal de forma indirecta en la página siguiente (énfasis mío) http://msdn.microsoft.com/en-us/library/0yw5843c.aspx :
La sintaxis de inicialización de signo igual es diferente de la sintaxis de estilo de función, aunque el código generado es idéntico en la mayoría de los casos. La diferencia es que cuando se usa la sintaxis de signo igual, el compilador tiene que comportarse como si se estuviera produciendo la siguiente secuencia de eventos:
- Creando un objeto temporal del mismo tipo que el objeto que se inicializa.
- Copiando el objeto temporal al objeto.
El constructor debe estar accesible antes de que el compilador pueda realizar estos pasos. Aunque el compilador puede eliminar los pasos de creación y copia temporal en la mayoría de los casos, un constructor de copia inaccesible hace que falle la inicialización de signo igual ( en / Za, / Ze (Deshabilitar extensiones de idioma) ).
Vea la respuesta de Ben Voigt para una solución alternativa que es una versión simplificada de boost::value_initialized
, como señaló litb en un comentario a la respuesta de Ben. Los documentos para boost::value_initalized
tienen una gran discusión del problema, la solución y algunas de las trampas de varios problemas del compilador.