c++ c++11 explicit-constructor

c++ - Evitar la conversión no deseada en el constructor.



c++11 explicit-constructor (5)

Según here , explicit :

Especifica constructores y operadores de conversión (desde C ++ 11) que no permiten conversiones implícitas o inicialización de copia.

Por lo tanto, ¿son estas dos técnicas idénticas?

struct Z { // ... Z(long long); // can initialize with a long long Z(long) = delete; // but not anything smaller }; struct Z { // ... explicit Z(long long); // can initialize ONLY with a long long };


Ellos no son los mismos.

Del borrador de trabajo estándar n4296 :

12.3.1 - [class.conv.ctor]:
1 Un constructor declarado sin el especificador de funciones explícito especifica una conversión de los tipos de sus parámetros al tipo de su clase. Tal constructor se llama un constructor de conversión .

2 Un constructor explícito construye objetos como constructores no explícitos, pero lo hace solo cuando se usa explícitamente la sintaxis de inicialización directa (8.5) o donde las conversiones (5.2.9, 5.4) se usan explícitamente. Un constructor predeterminado puede ser un constructor explícito; dicho constructor se utilizará para realizar la inicialización predeterminada o la inicialización de valores (8.5).

Seguido de un ejemplo de cada uno respectivamente:

struct X { X(int); X(const char*, int =0); X(int, int); }; void f(X arg) { X a = 1; // a = X(1) X b = "Jessie"; // b = X("Jessie",0) a = 2; // a = X(2) f(3); // f(X(3)) f({1, 2}); // f(X(1,2)) }

Con constructor explícito:

struct Z { explicit Z(); explicit Z(int); explicit Z(int, int); }; Z a; // OK: default-initialization performed Z a1 = 1; // error: no implicit conversion Z a3 = Z(1); // OK: direct initialization syntax used Z a2(1); // OK: direct initialization syntax used Z* p = new Z(1); // OK: direct initialization syntax used Z a4 = (Z)1; // OK: explicit cast used Z a5 = static_cast<Z>(1); // OK: explicit cast used Z a6 = { 3, 4 }; // error: no implicit conversion


No son idénticos.

Z z = 1LL;

Lo anterior funciona con la versión no explícita, pero no con la versión explícita.

La declaración del constructor de Z explícito no impide la conversión del argumento del constructor de otro tipo. Evita la conversión del argumento a Z sin llamar explícitamente al constructor.

A continuación se muestra un ejemplo de llamada explícita de constructor.

Z z = Z(1LL);


No, no son lo mismo. explicit no permite conversiones implícitas a ese tipo si ese constructor está seleccionado, las conversiones implícitas en los argumentos no importan. delete no permite ninguna construcción si ese constructor está seleccionado, y se puede usar para rechazar la conversión implícita de argumentos .

Así, por ejemplo:

struct X { explicit X(int ) { } }; void foo(X ) { } foo(4); // error, because X''s constructor is explicit foo(X{3}); // ok foo(X{''3''}); // ok, this conversion is fine

Eso es independiente de delete un constructor:

struct Y { Y(int ) { } Y(char ) = delete; }; void bar(Y ) { } bar(4); // ok, implicit conversion to Y since this constructor isn''t explicit bar(''4''); // error, this constructor is deleted bar(Y{''4''}); // error, doesn''t matter that we''re explicit

Las dos técnicas también son ortogonales. Si desea que un tipo no sea convertible implícitamente y solo se pueda construir desde exactamente un int , puede hacer ambas cosas:

struct W { explicit W(int ) { } template <class T> W(T ) = delete; }; void quux(W ); quux(4); // error, constructor is explicit quux(''4''); // error, constructor is deleted quux(4L); // error, constructor is deleted quux(W{''4''}); // error, constructor is deleted quux(W{5}); // ok


bloques explicit conversión implícita a su tipo .

Tu técnica =delete bloquea la conversión implícita de long a long long .

Estos son casi sin relación.

Hay 4 casos que ilustran la diferencia:

Z z = 1L; Z z = 1LL;

Es una conversión implícita de long y long long a Z

Z z = Z(1L); Z z = Z(1LL);

Es una conversión explícita de long y long long a Z

Bloques explicit Z(long long) :

Z z = 1L; Z z = 1LL;

mientras que Z(long)=delete bloques:

Z z = 1L; Z z = Z(1L);

explicit Z(long long) permite Z z = Z(1L) porque la conversión de long a long long es implícita, pero no está relacionada con la conversión explícita a Z que ocurre después.

Tenga en cuenta que una mezcla de explicit y =delete deja solo Z z=Z(1LL) como válida entre sus 4 versiones.

(Lo anterior supone una copia o movimiento válido ctor; si no, reemplace Z z=Z(...) con Z z(...) y el mismo resultado de las conclusiones).


struct Zb { Zb(long long) {}; // can initialize with a long long Zb(long) = delete; // but not anything smaller }; struct Za { // ... explicit Za(long long) {}; // can initialize ONLY with a long long }; int main() { Za((long long)10); // works Za((long)10); // works Zb((long long)10); // works Zb((long)10); // does not work return 0; }

Tu ejemplo requiere eliminación explícita.

En vivo: http://cpp.sh/4sqb