¿Qué significa la palabra clave explícita?

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

2387
23 сент. establecido por Skizz el 23 de septiembre 2008-09-23 16:58 '08 a las 4:58 pm 2008-09-23 16:58
@ 11 respuestas

El compilador tiene permitido realizar una conversión implícita para resolver los parámetros de la función. Esto significa que el compilador puede usar constructores llamados con un solo parámetro para convertir de un tipo a otro para obtener el tipo correcto para el 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 acepta un objeto Foo :

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

y aquí, donde se DoBar función DoBar .

 int main () { DoBar (42); } 

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

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

El prefijo de la palabra clave explicit constructor evita que el compilador use este constructor para las conversiones implícitas. Si se agrega a la clase anterior, se generará un error de compilación al llamar a la función DoBar (42) . Ahora debe llamar explícitamente a la conversión utilizando DoBar (Foo (42))

La razón por la que desea hacer esto es evitar la construcción accidental, que puede ocultar errores. Ejemplo reflexivo:

  • Tienes una clase MyString(int size) con un constructor que construye una cadena de un tamaño dado. Tiene una función de print(const MyString> y llama a print(3) (cuando en realidad tenía la intención de llamar a print("3") ). Usted espera que imprima "3", pero en su lugar imprime una cadena de longitud 3 vacía.
2778
23 сент. La respuesta se da a Skizz 23 Sep. 2008-09-23 16:59 '08 a las 4:59 pm 2008-09-23 16:59

Supongamos que tienes una clase de 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 en un int , y luego se llamará al constructor String(int) . Pero esto no es lo que el usuario podría pretender. Entonces, para prevenir 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 }; 
971
23 сент. La respuesta la da Eddie el 23 de septiembre. 2008-09-23 17:09 '08 a las 5:09 pm 2008-09-23 17:09

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

Por ejemplo, si tiene una clase de cadena con un constructor de String(const char* s) , tal vez eso es lo que quiere. Puede pasar const char* función que espera una String , y el compilador creará automáticamente un objeto de String temporal para usted.

Por otro lado, si tiene una clase de búfer, el constructor de Buffer(int size) toma el tamaño del búfer en bytes, probablemente no quiera que el compilador convierta fácilmente el int a Buffer . Para evitar esto, declara un constructor con la palabra clave explicit :

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

Por lo tanto,

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

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

 useBuffer(Buffer(4)); 

Por lo tanto, si su constructor de un solo parámetro convierte un parámetro en un objeto de su clase, es probable que no desee utilizar la palabra clave explicit . Pero si tienes un constructor que solo toma un parámetro, debes declararlo como explicit , para que el compilador no te sorprenda con conversiones inesperadas.

133
23 сент. La respuesta se da cjm 23 sep. 2008-09-23 19:37 '08 a las 7:37 pm 2008-09-23 19:37

Esta respuesta se refiere a la creación de un objeto con / sin un constructor explícito, ya que no se considera en 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 dos maneras:

 Foo bar1(10); Foo bar2 = 20; 

Dependiendo de la implementación, la segunda forma de crear una instancia de la clase Foo puede ser confusa, o no lo que el programador planeó. El prefijo explicit de la palabra clave al constructor genera un error de compilador en Foo bar2 = 20; ,

Normalmente es una buena práctica declarar a los constructores con un solo argumento como explicit , a menos que su implementación lo prohíba.

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

Ambos pueden ser utilizados como constructores con un argumento. Por lo tanto, puedes hacer esto explicit .

Por ejemplo, si intencionalmente no quieres que tu constructor con un argumento sea explícito, esto es si creas un functor (mira la estructura add_x declarada en esta respuesta). En este caso, un objeto se crea como add_x add30 = 30; probablemente tenga sentido

Aquí hay un buen registro de constructores explícitos.

34
08 окт. Responder Gautam 08 de octubre. 2013-10-08 17:43 '13 a las 17:43 2013-10-08 17:43

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

32
21 нояб. La respuesta la da SankararaoMajji el 21 de noviembre. 2012-11-21 05:36 '12 a las 5:36 am 2012-11-21 05:36

La palabra clave explicit acompaña a cualquiera

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

C ++ [class.conv.ctor]

1) Un constructor declarado sin un especificador de función 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 transformación.

2) El constructor explícito construye los objetos de la misma manera que los constructores implícitos, pero lo hace solo donde se usa explícitamente la sintaxis de inicialización directa (8.5) o donde se usan los lanzamientos (5.2.9, 5.4). El constructor predeterminado puede ser un constructor explícito; dicho constructor se utilizará para realizar la inicialización por defecto o la inicialización del valor (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) La función de transformación puede ser explícita (7.1.2), en cuyo caso se considera solo como una transformación personalizada para la inicialización directa (8.5). De lo contrario, las transformaciones definidas por el usuario no se limitan a la asignación y la inicialización.

Revisión

Las funciones de transformación explícita y los constructores solo se pueden usar para transformaciones explícitas (inicialización directa o operación de conversión explícita), mientras que los constructores implícitos y las funciones de transformación se pueden usar tanto para transformaciones explícitas como explícitas.

  

Un ejemplo del uso de las estructuras X, Y, Z y funciones foo, bar, baz :

Mire la pequeña configuración de estructuras y funciones para ver la diferencia entre 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 de diseñadores:

Conversión de 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 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 funciones de conversión o constructores explicit ?

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

Considere la estructura V convertida a int , la estructura U implícitamente constructiva de V y la función f sobrecargada para U y bool respectivamente.

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

La llamada f ambigua cuando pasa un objeto de tipo V

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

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

Si el constructor U , o la función de conversión V es explicit , entonces no habrá ambigüedad, ya que solo se considerará la conversión implícita. Si ambos son explícitos, llamar a f usando un objeto de tipo V debe realizarse usando una conversión explícita o una operación de conversión.

Los constructores de conversión y las funciones de conversión implícita pueden llevar a un comportamiento inesperado.

Considere la función de impresión de un vector:

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

Si el tamaño del constructor del vector no fuera explícito, tal función podría llamarse:

 print_intvector(3); 

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

El uso de una palabra clave explícita en la interfaz de clase obliga a la interfaz de usuario a especificar explícitamente la conversión deseada.

Como escribe Bjarne Straustrup (en C ++ Programming >std::duration no puede construirse implícitamente a partir de un número primo:

Si sabes a qué te refieres, sé franco al respecto.

31
11 июля '15 в 2:48 2015-07-11 02:48 La respuesta la da Pixelchemist el 11 de julio de 2015 a las 2:48 2015-07-11 02:48

explicit -keyword se puede usar para forzar a un constructor a llamarse explícitamente.

 class C{ public: explicit C(void) = default; }; int main(void){ C c(); return 0; } 

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

La clave de palabra clave explicit también se puede utilizar en operaciones de conversión de tipos personalizadas:

 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 aplica solo conversiones explícitas a una acción, por lo tanto bool b = c; En este caso será inválido. En tales situaciones, explicit palabra clave explicit puede ayudar al programador a evitar aumentos implícitos y no intencionados. Este uso está estandarizado en C ++ 11 .

25
14 мая '13 в 12:28 2013-05-14 12:28 Helixirr da la respuesta el 14 de mayo de 2013 a las 12:28. 2013-05-14 12:28

Esto ya ha sido discutido ( lo que es el constructor explícito ). Pero debo decir que le faltan las descripciones detalladas que se encuentran aquí.

Además, siempre es una buena práctica de codificación hacer tus propios constructores de argumentos (incluidos aquellos que tienen valores predeterminados para arg2, arg3, ...), como se ha dicho. Como siempre con C ++: si no lo haces, querrás que lo hagas ...

Otra buena práctica para las clases es hacer que la copia y la asignación sean privadas (también deshabilitadas) si realmente no es necesario implementarlo. Esto evita las posibles copias de los punteros cuando se utilizan métodos que C ++ crea de forma predeterminada. Otra forma de hacer esto es obtener un impulso: no se puede copiar.

17
02 окт. la respuesta se da fmuecke 02 oct. 2009-10-02 01:00 '09 a la 1:00 am 2009-10-02 01:00

Link Cpp siempre es útil !!! Los detalles sobre el especificador explícito se pueden encontrar aquí . Puede que tenga que mirar las conversiones implícitas y la inicialización de la copia .

Mirada rapida

Un especificador explícito indica que el constructor o la función de conversión (desde C ++ 11) no permite la conversión implícita o la inicialización de la copia.

Un ejemplo es el siguiente:

 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
20 авг. La respuesta se da autoboot 20 ago. 2016-08-20 15:45 '16 a las 15:45 2016-08-20 15:45

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

Un especificador de función explícito controla un tipo de conversión implícita no deseada. Solo se puede utilizar en declaraciones de constructor en una declaración de clase. Por ejemplo, además del constructor, los constructores de la siguiente clase son la transformación Constructores.

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

Los siguientes anuncios son legales:

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

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

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

Por ejemplo, si declara una clase como:

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

Solo puede asignar valores que correspondan a valores de tipo de clase.

Por ejemplo, las siguientes afirmaciones 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
20 дек. La respuesta se da coding_ninza 20 dic. 2017-12-20 15:19 '17 a las 15:19 2017-12-20 15:19