¿Qué significa la palabra clave explícita?


¿Qué significa la palabra clave explicit en C++?

Author: Ronny Brendel, 2008-09-23

11 answers

Al compilador se le permite hacer una conversión implícita para resolver los parámetros a una función. Lo que esto significa es que el compilador puede usar constructores llamables con un único parámetro para convertir de un tipo a otro con el fin de 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 un objeto Foo:

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

Y aquí está donde se llama a la función DoBar.

int main ()
{
  DoBar (42);
}

El argumento no es un objeto Foo, sino un int. Sin embargo, existe un constructor para Foo que toma un int por lo que este constructor se puede usar para convertir el parámetro al tipo correcto.

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

Anteponer la palabra clave explicit al constructor evita que el compilador use ese constructor para conversiones implícitas. Agregarlo a la clase anterior será cree un error de compilador en la llamada a la función DoBar (42). Ahora es necesario pedir la conversión explícitamente con DoBar (Foo (42))

La razón por la que podría querer hacer esto es para evitar la construcción accidental que puede ocultar errores. Ejemplo artificial:

  • Tienes una clase MyString(int size) con un constructor que construye una cadena del tamaño dado. Tienes una función print(const MyString&), y llamas print(3) (cuando en realidad pretendes llamar print("3")). Se espera que imprima "3", pero imprime una cadena vacía de longitud 3 en su lugar.
 2823
Author: Skizz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-24 18:46:20

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 lo intentas:

String mystring = 'x';

El carácter 'x' se convertirá implícitamente a int y luego se llamará al constructor String(int). Pero, esto no es lo que el usuario podría haber pretendido. Por lo tanto, 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
};
 982
Author: Eddie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 09:28:46

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

Por ejemplo, si tienes una clase string con constructor String(const char* s), eso es probablemente exactamente lo que quieres. Puede pasar un const char* a una función esperando un String, y el compilador construirá automáticamente un objeto temporal String para usted.

Por otro lado, si tiene una clase de búfer cuyo constructor Buffer(int size) toma el tamaño del búfer en bytes, probablemente no quiera que el compilador convierta silenciosamente int s en Buffer s. Para evitar eso, declare el constructor con la palabra clave explicit:

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 desea pasar un objeto temporal Buffer, debe hacerlo explícitamente:

useBuffer(Buffer(4));

En resumen, si su constructor de un solo parámetro convierte el parámetro en un objeto de tu clase, probablemente no quieras usar la palabra clave explicit. Pero si tienes un constructor que simplemente toma un solo parámetro, deberías declararlo como explicit para evitar que el compilador te sorprenda con conversiones inesperadas.

 136
Author: cjm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 16:37:12

Esta respuesta es acerca de 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. Anteponer la palabra clave explicit al constructor generaría un error de compilador en Foo bar2 = 20;.

Es por lo general una buena práctica declarar constructores de un solo argumento 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 por defecto para el segundo parámetro en adelante

Se pueden usar ambos como constructores de un solo argumento. Así que es posible que desee hacer estos también explicit.

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

Aquí es una buena descripción de constructores explícitos.

 36
Author: Gautam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:32

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

 33
Author: SankararaoMajji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-14 16:40:06

La palabra clave explicit acompaña a

  • un constructor de clase X que no se puede usar para convertir implícitamente el primer parámetro (solo cualquiera) a tipo X

C++ [clase.conv.ctor]

1) Un constructor declarado sin el especificador de función explicit 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) An explicit constructor construye objetos al igual que los constructores no explícitos, pero lo hace solo donde la sintaxis de inicialización directa (8.5) o donde se usan explícitamente los casts (5.2.9, 5.4). Un constructor predeterminado puede ser un constructor explícito; tal constructor se utilizará para realizar la inicialización predeterminada o la inicialización de valor (8.5).

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

C++ [clase.conv.fct]

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

Panorama general

Las funciones de conversión explícitas 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 tanto para conversiones implícitas como 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 las conversiones explicit y no-explicit.

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

Objeto inicialización:

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 con respecto a las 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 qué 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.

Considere una estructura V, convertible a int, una estructura U implícitamente construible a partir de V y una función f sobrecargado para 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 ambigua 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 objeto V 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 la conversión no explícita sería considerar. 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 conversión.

Los constructores de conversión y las funciones de conversión no explícitas pueden conducir a un comportamiento inesperado.

Considere una función que imprime algún vector:

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

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

print_intvector(3);

¿Qué haría uno espera de tal llamada? ¿Una línea que contenga 3 o tres líneas que contengan 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 una conversión deseada.

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

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

 33
Author: Pixelchemist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-12 11:29:40

La palabra clave explicit-se puede usar para forzar que un constructor sea llamado explícitamente.

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

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

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

La palabra clave explicit-también se puede usar 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 hace que solo los casts explícitos sean válidos, por lo que bool b = c; sería un cast no válido en este caso. En situaciones como estas explicit - palabra clave puede ayudar al programador a evitar lanzamientos implícitos e involuntarios. Este uso ha sido estandarizado en C++11.

 25
Author: Helixirr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-13 09:08:04

Esto ya ha sido discutido ( qué es constructor explícito). Pero debo decir, que carece de las descripciones detalladas que se encuentran aquí.

Además, siempre es una buena práctica de codificación hacer que sus constructores de un argumento (incluidos aquellos con valores predeterminados para arg2,arg3,...) como ya se ha dicho. Como siempre con C++: si no lo haces, desearás haberlo hecho...

Otra buena práctica para las clases es hacer privada la construcción y asignación de copias (también conocida como desactivarla) a menos que realmente necesites implementarlo. Esto evita tener eventuales copias de punteros cuando se utilizan los métodos que C++ creará por defecto. Otra forma de hacer esto es derivar de boost::noncopyable.

 17
Author: fmuecke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:28

La referencia Cpp siempre es útil!!! Los detalles sobre el especificador explícito se pueden encontrar aquí. Es posible que tenga que mirar conversiones implícitas y inicialización de copia también.

Mirada rápida

El especificador explícito especifica que un constructor o función de conversión (desde C++11) no permite conversiones implícitas o inicialización de copia.

Ejemplo como sigue:

struct A
{
    A(int) { }      // converting constructor
    A(int, int) { } // converting constructor (C++11)
    operator bool() const { return true; }
};

struct B
{
    explicit B(int) { }
    explicit B(int, int) { }
    explicit operator bool() const { return true; }
};

int main()
{
    A a1 = 1;      // OK: copy-initialization selects A::A(int)
    A a2(2);       // OK: direct-initialization selects A::A(int)
    A a3 {4, 5};   // OK: direct-list-initialization selects A::A(int, int)
    A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
    A a5 = (A)1;   // OK: explicit cast performs static_cast
    if (a1) cout << "true" << endl; // OK: A::operator bool()
    bool na1 = a1; // OK: copy-initialization selects A::operator bool()
    bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization

//  B b1 = 1;      // error: copy-initialization does not consider B::B(int)
    B b2(2);       // OK: direct-initialization selects B::B(int)
    B b3 {4, 5};   // OK: direct-list-initialization selects B::B(int, int)
//  B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
    B b5 = (B)1;   // OK: explicit cast performs static_cast
    if (b5) cout << "true" << endl; // OK: B::operator bool()
//  bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
    bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
}
 15
Author: selfboot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-20 12:45:16

Constructores de conversión explícitos (solo en C++)

El especificador de función explícito controla el tipo implícito no deseado conversion. Solo se puede utilizar en declaraciones de constructores dentro de una declaración de clase. Por ejemplo, excepto por el valor predeterminado constructor, los constructores en la siguiente clase son conversión constructor.

class A
{
public:
    A();
    A(int);
    A(const char*, int = 0);
};

Las siguientes declaraciones son legales:

A c = 1;
A d = "Venditti";

La primera declaración es equivalente a A c = A( 1 );.

Si declara el constructor de la clase como explicit, las declaraciones anteriores serían ilegales.

Por ejemplo, si declara la clase como:

class A
{
public:
    explicit A();
    explicit A(int);
    explicit A(const char*, int = 0);
};

Solo puede asignar valores que coincidan con los valores del tipo de clase.

Por ejemplo, las siguientes declaraciones son legales:

  A a1;
  A a2 = A(1);
  A a3(1);
  A a4 = A("Venditti");
  A* p = new A(1);
  A a5 = (A)1;
  A a6 = static_cast<A>(1);
 10
Author: coding_ninza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 09:32:49

Los constructores anexan la conversión implícita. Para suprimir esta conversión implícita es necesario declarar un constructor con un parámetro explícito.

En C++11 también puede especificar un" tipo de operador () " con dicha palabra clave http://en.cppreference.com/w/cpp/language/explicit Con tal especificación se puede usar operador en términos de conversiones explícitas e inicialización directa del objeto.

P.d. Cuando se utilizan transformaciones definidas POR EL USUARIO (a través de constructores y tipo operador de conversión) solo se permite un nivel de conversiones implícitas utilizadas. Pero puedes combinar estas conversiones con otras conversiones de idiomas

  • subir rangos integrales (char a int, float a double);
  • conversiones estándar (int a doble);
  • convierte punteros de objetos a clase base y a void*;
 4
Author: bruziuz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-23 09:26:52