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