sirve que por para hacer defecto constructores como c++ c++11 c++14 language-lawyer

que - default c++



Constructor por defecto explĂ­cito (1)

Este código compila bien con GCC 5.X, MSVC, pero GCC 6.X da error:

"la conversión a ''a'' de la lista de inicializadores usaría el constructor explícito ''a :: a ()''" , el argot "el constructor elegido es explícito en la inicialización de la copia" .

Eliminar explicit o cambiar a ac{} soluciona el problema, pero tengo curiosidad de por qué funciona de esta manera.

class a { public: explicit a () {} }; struct b { a c; }; int main() { b d{}; }


b es un aggregate . Cuando lo inicializa utilizando una lista de inicializadores, los elementos de la lista inicializarán los primeros n miembros del agregado, donde n es el número de elementos de la lista. Los elementos restantes del agregado son copia-lista-inicializada .

Entonces, en su ejemplo, c será copia-lista-inicializada , pero eso está mal formado si el constructor elegido es explicit , por lo tanto, el error.

Las citas estándar relevantes son

[dcl.init.aggr]/3

Cuando un agregado se inicializa con una lista de inicializadores como se especifica en [dcl.init.list], los elementos de la lista de inicializadores se toman como inicializadores para los elementos del agregado. Los elementos explícitamente inicializados del agregado se determinan de la siguiente manera:
...
- Si la lista de inicializadores es una lista de inicializadores , los elementos inicializados explícitamente del agregado son los primeros n elementos del agregado, donde n es el número de elementos en la lista de inicializadores.
- De lo contrario, la lista de inicializadores debe ser {} , y no hay elementos inicializados explícitamente.

[dcl.init.aggr]/5

Para un agregado no sindicalizado, cada elemento que no es un elemento explícitamente inicializado se inicializa de la siguiente manera:
...
- De lo contrario, si el elemento no es una referencia, el elemento se inicializa con copia desde una lista de inicialización vacía ([dcl.init.list]).

El efecto de la copia que inicializa c de una lista de inicializador vacía se describe en

[dcl.init.list]/3

La inicialización de lista de un objeto o referencia de tipo T se define de la siguiente manera:
...
- De lo contrario, si la lista de inicializadores no tiene elementos y T es un tipo de clase con un constructor predeterminado, el objeto se inicializa con valores.

[dcl.init]/8

Para inicializar con valor un objeto de tipo T significa:
...
- si T es un tipo de clase (posiblemente cv calificado) sin un constructor predeterminado ([class.ctor]) o un constructor predeterminado que es proporcionado o eliminado por el usuario, entonces el objeto está inicializado por defecto;

[dcl.init]/7

Para inicializar por defecto un objeto de tipo T significa:
- Si T es un tipo de clase (posiblemente cv-calificado), se consideran constructores. Los constructores correspondientes se enumeran ([over.match.ctor]), y el mejor para el inicializador () se elige mediante resolución de sobrecarga. El constructor así seleccionado se llama, con una lista de argumentos vacía, para inicializar el objeto.

[over.match.ctor]

... Para la inicialización de la copia, las funciones candidatas son todos los constructores de conversión de esa clase.

[class.conv.ctor]/1

Un constructor declarado sin el especificador de función explicit especifica una conversión de los tipos de sus parámetros (si los hay) al tipo de su clase. Tal constructor se llama un constructor de conversión .

En el ejemplo anterior, a no tiene constructores de conversión, por lo que la resolución de sobrecarga falla. El ejemplo (no normativo) en [class.conv.ctor] / 2 incluso contiene un caso muy similar

struct Z { explicit Z(); explicit Z(int); explicit Z(int, int); }; Z c = {}; // error: copy-list-initialization

Puede evitar el error proporcionando un inicializador de miembro predeterminado para c

struct b { a c{}; // direct-list-initialization, explicit ctor is OK };