c++ c++11 language-lawyer copy-constructor uniform-initialization

c++ - Copia explícita de constructor e inicialización uniforme.



c++11 language-lawyer (1)

Ha encontrado un caso que se resolvió mediante la resolución del problema Core 1467 inmediatamente después de finalizar C ++ 14.

Primero notemos que la clase foo es un agregado. Tu código está haciendo la inicialización directa de listas para foo . Las reglas para la inicialización de listas están en [8.5.4p3].

En C ++ 14 (citando de N4140, el borrador de trabajo más cercano a la norma publicada), el párrafo anterior comenzó con:

La inicialización de lista de un objeto o referencia de tipo T se define de la siguiente manera:

  • Si T es un agregado, se realiza la inicialización agregada (8.5.1).

[...]

Por lo tanto, si su clase es un agregado, el compilador intenta realizar la inicialización del agregado, que falla.

Esto se reconoció como un problema y se solucionó en el borrador de trabajo. Citando de la versión actual, N4527, el párrafo mencionado ahora comienza con:

La inicialización de lista de un objeto o referencia de tipo T se define de la siguiente manera:

  • Si T es un tipo de clase y la lista de inicializadores tiene un solo elemento de tipo cv U , donde U es T o una clase derivada de T , el objeto se inicializa desde ese elemento (mediante copia-inicialización para copiar-lista-inicialización, o por inicialización directa para inicialización de lista directa).
  • De lo contrario, si T es una matriz de caracteres y la lista de inicializadores tiene un solo elemento que es un literal de cadena correctamente tipificado (8.5.2), la inicialización se realiza como se describe en esa sección.
  • De lo contrario, si T es un agregado, se realiza la inicialización agregada (8.5.1).

[...]

Su ejemplo ahora se encuentra dentro del caso descrito por el primer punto de viñeta, y foo se inicializa en lista directa utilizando el constructor de copia predeterminado (independientemente de si es explicit , ya que es una inicialización directa).

Es decir ... si el compilador implementa la resolución en el informe de defectos.

  • GCC 5.2.0 (y el tronco 6.0.0) parece hacerlo, pero parece tener un error relacionado con eso explicit .
  • Clang 3.6.0 no lo hace, pero 3.8.0 tronco lo hace, y lo hace correctamente ( explicit no importa).
  • MSVC 14 sí, pero IntelliSense en el IDE no (garabatos debajo de la bar : parece que el compilador EDG utilizado por IntelliSense tampoco se actualizó).

Actualización: desde que se escribió esta respuesta, el borrador de trabajo se ha modificado aún más en un par de formas que son relevantes para el ejemplo de la pregunta y la explicación anterior:

  • CWG 2137 indica que la primera viñeta en el párrafo citado anteriormente fue un poco demasiado lejos al aplicar esa excepción a todos los tipos de clase (las notas de problemas contienen un ejemplo relevante). El comienzo de la bala ahora dice:
    • Si T es una clase agregada y [...]
  • La resolución de CWG 1518 contenida en el documento P0398R0 indica que las clases que declaran un constructor explicit (incluso por defecto) ya no son agregados.

Esto no cambia el hecho de que, una vez implementados todos los cambios, el ejemplo de la pregunta está destinado a funcionar, con o sin el explicit ; vale la pena saber que los mecanismos subyacentes que lo hacen funcionar han cambiado ligeramente.

Tenga en cuenta que todos estos cambios son resoluciones de informes de defectos, por lo que se supone que deben aplicarse cuando los compiladores también están en modo C ++ 14 y C ++ 11.

Los constructores de copia explícita no permiten algo como Foo foo = bar; , y hacer cumplir el uso de la copia como Foo foo(bar); . Además, los constructores de copia explícita también rechazan devolver objetos por valor de una función. Sin embargo, traté de reemplazar la inicialización de la copia por llaves, como así

struct Foo { Foo() = default; explicit Foo(const Foo&) = default; }; int main() { Foo bar; Foo foo{bar}; // error here }

y estoy recibiendo el error (g ++ 5.2)

error: no hay función coincidente para la llamada a ''Foo :: Foo (Foo &)''

o (clang ++)

error: exceso de elementos en struct initializer

La eliminación de lo explicit hace que el código sea compilable en g ++, clang ++ aún falla con el mismo error (gracias @Steephen). ¿Que está pasando aqui? ¿Se considera la inicialización uniforme como un constructor de lista de inicializadores (que supera a todos los demás)? Pero si ese es el caso, ¿por qué el programa se compila cuando el constructor de copia no es explícito?