En C++, si un constructor se puede llamar con un solo argumento, se considera implicit (implícito) por defecto. Este constructor puede ser utilizado para conversión de tipos implícita. Para protegerse de esto, se utiliza la palabra clave explicit.
explicit prohíben las conversiones implícitas.El uso de explicit permite evitar conversiones inesperadas que pueden ocurrir, por ejemplo, al pasar un argumento de un tipo no coincidente a una función o al inicializar una variable.
Ejemplo:
struct Foo { explicit Foo(int x) { /* ... */ } }; Foo a = 10; // Error, explicit prohíbe la inicialización implícita Foo b(10); // OK
Si no hubiera explicit, la línea Foo a = 10; estaría permitida y podría conducir a errores inesperados.
Pregunta: ¿Todos los constructores, declarados con explicit, no pueden ser llamados al inicializar con = ?
Respuesta común: Sí, explicit prohíbe cualquier inicialización con =.
Respuesta correcta: explicit solo prohíbe conversiones implícitas. Con la inicialización directa (ClassName obj(param);) se llama al constructor explicit. Con inicialización por copia (ClassName obj = param;) — no.
Ejemplo:
struct A { explicit A(int) {} }; A x = 1; // Error A y(1); // OK
Historia: En el proyecto se escribió un constructor sin explicit, permitiendo la inicialización a través de tipos, lo que llevaba a conversiones automáticas inesperadas de los argumentos de las funciones. Esto causó la aparición de errores ocultos y dificultó la depuración.
Historia: Un desarrollador no puso explicit en el constructor de un contenedor, y de repente se comenzó a llamar al constructor por defecto al asignar o pasar otros tipos. Resultado: lógica de creación de objetos incorrecta y comportamiento impredecible.
Historia: Un programador inexperto declaró un constructor explicit, pero se sorprendió de que la inicialización directa con paréntesis funcionara, pensando que explicit bloqueaba también eso. Esto llevó a no utilizar patrones seguros convenientes y a un código adicional en el proyecto.