Pregunta ¿Qué significa la palabra clave explícita?


Lo que hace el explicit ¿significa la palabra clave en C ++?


2363
2017-09-23 13:58


origen


Respuestas:


El compilador puede hacer una conversión implícita para resolver los parámetros de una función. Lo que esto significa es que el compilador puede usar constructores que se pueden llamar con un solo parámetro para convertir de un tipo a otro para obtener el tipo correcto para un parámetro.

Aquí hay una clase de ejemplo con un constructor que se puede usar para conversiones implícitas:

class Foo
{
public:
  // single parameter constructor, can be used as an implicit conversion
  Foo (int foo) : m_foo (foo) 
  {
  }

  int GetFoo () { return m_foo; }

private:
  int m_foo;
};

Aquí hay una función simple que toma una Foo objeto:

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

y aquí es donde el DoBar la función se llama.

int main ()
{
  DoBar (42);
}

El argumento no es un Foo objeto, pero una int. Sin embargo, existe un constructor para Foo eso toma una int entonces este constructor se puede usar para convertir el parámetro al tipo correcto.

El compilador puede hacer esto una vez para cada parámetro.

Prefijo de explicit la palabra clave para el constructor evita que el compilador use ese constructor para las conversiones implícitas. Si lo agrega a la clase anterior, se creará un error de compilación en la llamada a la función DoBar (42). Ahora es necesario llamar a la conversión de forma explícita con DoBar (Foo (42))

La razón por la que puede querer hacer esto es evitar construcciones accidentales que puedan ocultar errores. Ejemplo de ejemplo:

  • Usted tiene un MyString(int size) clase con un constructor que construye una cadena del tamaño dado. Tienes una función print(const MyString&)y tu llamas print(3) (cuando tú actualmente destinado a llamar print("3")) Espera que imprima "3", pero imprime una cadena vacía de 3 en su lugar.

2749
2017-09-23 14:09



Supongamos que tienes una clase String:

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

Ahora, si intentas:

String mystring = 'x';

El personaje 'x' se convertirá implícitamente en int y luego el String(int) constructor será llamado. Pero esto no es lo que el usuario podría haber intentado. Entonces, para evitar tales condiciones, definiremos el constructor como explicit:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

962
2017-09-23 16:37



En C ++, un constructor con un solo parámetro requerido se considera una función de conversión implícita. Convierte el tipo de parámetro al tipo de clase. Que esto sea bueno o no depende de la semántica del constructor.

Por ejemplo, si tiene una clase de cadena con constructor String(const char* s), eso es probablemente lo que quieres. Puedes pasar un const char* a una función esperando un String, y el compilador construirá automáticamente un String objeto para ti

Por otro lado, si tienes una clase de buffer cuyo constructor Buffer(int size) toma el tamaño del búfer en bytes, probablemente no quieras que el compilador gire silenciosamente ints en Buffers. Para evitar eso, declaras el constructor con el explicit palabra clave:

class Buffer { explicit Buffer(int size); ... }

De esa manera,

void useBuffer(Buffer& buf);
useBuffer(4);

se convierte en un error en tiempo de compilación. Si quieres pasar un temporal Buffer objeto, tienes que hacerlo explícitamente:

useBuffer(Buffer(4));

En resumen, si su constructor de parámetro único convierte el parámetro en un objeto de su clase, probablemente no quiera usar el explicit palabra clave. Pero si tiene un constructor que simplemente toma un solo parámetro, debe declararlo como explicitpara evitar que el compilador lo sorprenda con conversiones inesperadas.


130
2017-10-08 14:43



Esta respuesta trata sobre la creación de objetos con / sin un constructor explícito, ya que no está cubierto en las otras respuestas.

Considere la siguiente clase sin un constructor explícito:

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

Los objetos de la clase Foo se pueden crear de 2 maneras:

Foo bar1(10);

Foo bar2 = 20;

Dependiendo de la implementación, la segunda manera de instanciar la clase Foo puede ser confusa, o no, lo que el programador pretendía. Prefijo de explicit palabra clave para el constructor generaría un error de compilación en Foo bar2 = 20;.

Es generalmente buena práctica para declarar constructores de argumento único como explicit, a menos que su implementación lo prohíba específicamente.

Tenga en cuenta también que los constructores con

  • argumentos por defecto para todos los parámetros, o
  • argumentos predeterminados para el segundo parámetro en adelante

ambos pueden usarse como constructores de argumento único. Por lo que es posible que desee hacer estos también explicit.

Un ejemplo cuando deliberadamente no quiere hacer explícito su constructor de argumento único si está creando un funtor (mire la estructura 'add_x' declarada en esta responder). En tal caso, crear un objeto como add_x add30 = 30; probablemente tendría sentido.

aquí es un buen artículo sobre constructores explícitos.


34
2017-11-21 02:36



los explicit palabra clave convierte un constructor de conversión en un constructor sin conversión. Como resultado, el código es menos propenso a errores.


31
2017-07-10 23:48



La palabra clave explicit acompaña a cualquiera

  • un constructor de clase X que no puede usarse para convertir implícitamente el primer parámetro (solo uno) al tipo X

C ++ [class.conv.ctor]

1) Un constructor declarado sin el especificador de función explícita especifica una conversión de los tipos de sus parámetros al tipo de su clase. Tal constructor se llama constructor de conversión.

2) Un constructor explícito construye objetos al igual que los constructores no explícitos, pero solo lo hace cuando se usan explícitamente la sintaxis de inicialización directa (8.5) o los moldes (5.2.9, 5.4). Un constructor predeterminado puede ser un constructor explícito; dicho constructor se usará para realizar la inicialización-inicialización o la inicialización-valorización   (8.5).

  • o una función de conversión que solo se considera para la inicialización directa y la conversión explícita.

C ++ [class.conv.fct]

2) Una función de conversión puede ser explícita (7.1.2), en cuyo caso solo se considera una conversión definida por el usuario para la inicialización directa (8.5). De lo contrario, las conversiones definidas por el usuario no están restringidas al uso en asignaciones   e inicializaciones.

Visión de conjunto

Las funciones de conversión explícita y los constructores solo se pueden usar para conversiones explícitas (inicialización directa u operación de conversión explícita) mientras que los constructores no explícitos y las funciones de conversión se pueden usar para conversiones implícitas y explícitas.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Ejemplo usando estructuras X, Y, Z y funciones foo, bar, baz:

Veamos una pequeña configuración de estructuras y funciones para ver la diferencia entre explicity noexplicit conversiones

struct Z { };

struct X { 
  explicit X(int a); // X can be constructed from int explicitly
  explicit operator Z (); // X can be converted to Z explicitly
};

struct Y{
  Y(int a); // int can be implicitly converted to Y
  operator Z (); // Y can be implicitly converted to Z
};

void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }

Ejemplos con respecto al constructor:

Conversión de un argumento de función:

foo(2);                     // error: no implicit conversion int to X possible
foo(X(2));                  // OK: direct initialization: explicit conversion
foo(static_cast<X>(2));     // OK: explicit conversion

bar(2);                     // OK: implicit conversion via Y(int) 
bar(Y(2));                  // OK: direct initialization
bar(static_cast<Y>(2));     // OK: explicit conversion

Inicialización de objetos:

X x2 = 2;                   // error: no implicit conversion int to X possible
X x3(2);                    // OK: direct initialization
X x4 = X(2);                // OK: direct initialization
X x5 = static_cast<X>(2);   // OK: explicit conversion 

Y y2 = 2;                   // OK: implicit conversion via Y(int)
Y y3(2);                    // OK: direct initialization
Y y4 = Y(2);                // OK: direct initialization
Y y5 = static_cast<Y>(2);   // OK: explicit conversion

Ejemplos de funciones de conversión:

X x1{ 0 };
Y y1{ 0 };

Conversión de un argumento de función:

baz(x1);                    // error: X not implicitly convertible to Z
baz(Z(x1));                 // OK: explicit initialization
baz(static_cast<Z>(x1));    // OK: explicit conversion

baz(y1);                    // OK: implicit conversion via Y::operator Z()
baz(Z(y1));                 // OK: direct initialization
baz(static_cast<Z>(y1));    // OK: explicit conversion

Inicialización de objetos:

Z z1 = x1;                  // error: X not implicitly convertible to Z
Z z2(x1);                   // OK: explicit initialization
Z z3 = Z(x1);               // OK: explicit initialization
Z z4 = static_cast<Z>(x1);  // OK: explicit conversion

Z z1 = y1;                  // OK: implicit conversion via Y::operator Z()
Z z2(y1);                   // OK: direct initialization
Z z3 = Z(y1);               // OK: direct initialization
Z z4 = static_cast<Z>(y1);  // OK: explicit conversion

Por que usar explicit funciones de conversión o constructores?

Los constructores de conversión y las funciones de conversión no explícitas pueden introducir ambigüedad.

Considera una estructura V, convertible a int, una estructura U implícitamente construible desde V y una función f sobrecargado por U y bool respectivamente.

struct V {
  operator bool() const { return true; }
};

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

Una llamada a f es ambiguo si se pasa un objeto de tipo V.

V x;
f(x);  // error: call of overloaded 'f(V&)' is ambiguous

El compilador no sabe si usar el constructor de U o la función de conversión para convertir el V objeto en un tipo para pasar a f.

Si el constructor de U o la función de conversión de V sería explicit, no habría ambigüedad ya que solo se consideraría la conversión no explícita. Si ambos son explícitos, la llamada a f usando un objeto de tipo V tendría que hacerse usando una conversión explícita o una operación de lanzamiento.

Los constructores de conversión y las funciones de conversión no explícitas pueden llevar a comportamientos inesperados.

Considere una función imprimiendo algún vector:

void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }

Si el tamaño-constructor del vector no fuera explícito, sería posible llamar a la función de esta manera:

print_intvector(3);

¿Qué se esperaría de tal llamada? Una línea que contiene 3 o tres líneas que contienen 0? (Donde el segundo es lo que sucede)

El uso de la palabra clave explícita en una interfaz de clase obliga al usuario de la interfaz a ser explícito sobre la conversión deseada.

Como dice Bjarne Stroustrup (en "The C ++ Programming Language", 4th Ed., 35.2.1, pp. 1011) sobre la pregunta por qué std::duration no se puede construir implícitamente a partir de un número simple:

Si sabes lo que quieres decir, sé explícito al respecto.


31
2018-05-14 09:28



los explicit-keyword se puede usar para imponer un constructor que se llamará explícitamente.

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

el explicit-palabra clave delante del constructor C(void) le dice al compilador que solo se permite la llamada explícita a este constructor.

los explicit-keyword también se puede utilizar en operadores de conversión de tipo definidos por el usuario:

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

Aquí, explicit-keyword exige que solo los lanzamientos explícitos sean válidos, por lo que bool b = c; Sería un molde inválido en este caso. En situaciones como estas explicit-keyword puede ayudar al programador a evitar conversiones implícitas e involuntarias. Este uso ha sido estandarizado en C ++ 11.


25
2017-10-01 22:00