¿Qué significa la palabra clave explícita?

¿Qué hace el explicit palabra clave en C ++?

preguntado el 23 de septiembre de 08 a las 11:09

Solo quiero señalar a cualquier persona nueva que venga que desde C ++ 11, explicit se puede aplicar a más que solo constructores. Ahora también es válido cuando se aplica a los operadores de conversión. Di que tienes una clase BigInt con un operador de conversión a int y un operador de conversión explícito a std::string por cualquier razón. Podrás decir int i = myBigInt;, pero tendrás que emitir explícitamente (usando static_cast, preferiblemente) para decir std::string s = myBigInt;. -

¿No puede explícitamente referirse también a la asignación? (es decir int x(5);) -

@curiousguy, No existe una conversión implícita explícita. -

@curiousguy, no es inherentemente una conversión implícita. Poniendo explicit allí declara una conversión explícita a un tipo. No hay implícitos involucrados en el proceso. -

@Milan, Sí, eso es exactamente. Si busca más información, esta respuesta lo escribe de manera más formal. Tenga en cuenta que bool es especial en este sentido. Esas respuestas y la búsqueda de "operadores de conversión explícitos" lo llevarán a más reseñas sobre esta función y serán más adecuadas que una cadena de comentarios. -

11 Respuestas

El compilador puede realizar una conversión implícita para resolver los parámetros en una función. Lo que esto significa es que el compilador puede usar constructores invocables con un parámetro único 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 un Foo :

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 un int. Sin embargo, existe un constructor para Foo eso toma un int por lo que este constructor se puede utilizar para convertir el parámetro al tipo correcto.

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

Prefijando el explicit palabra clave al constructor evita que el compilador utilice ese constructor para conversiones implícitas. Agregarlo a la clase anterior creará un error del compilador en la llamada a la función DoBar (42). Ahora es necesario llamar a la conversión explícitamente con DoBar (Foo (42))

La razón por la que podría querer hacer esto es para evitar construcciones accidentales que puedan ocultar errores.
Ejemplo elaborado:

  • Tiene 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 usted Realmente destinado a llamar print("3")). Espera que imprima "3", pero en su lugar imprime una cadena vacía de longitud 3.

Respondido el 01 de Septiembre de 20 a las 08:09

buena redacción, es posible que desee mencionar varios argumentos con parámetros predeterminados que también pueden actuar como un solo argumento, por ejemplo, Object (const char * name = NULL, int otype = 0). - maccullt

Creo que también debería mencionarse que uno debería considerar hacer explícitos los constructores de un solo argumento inicialmente (más o menos automáticamente), y eliminar la palabra clave explícita solo cuando se desea la conversión implícita por diseño. Creo que los constructores deberían ser explícitos por defecto con una palabra clave 'implícita' para permitirles trabajar como conversiones implícitas. Pero no es así. - miguel rebabas

@thecoshman: No declaras un parámetro explicit - declaras un constructor explicit. Pero sí: tus parámetros de tipo Foo tiene que ser construido explicitely, no se construirán en silencio simplemente conectando los parámetros de su constructor en la función. - cristiano severin

Solo un FYI que al llamar a "print (3)" en tu ejemplo, la función debe ser "print (const MyString &"). La "const" es obligatoria aquí porque 3 se convierte en un objeto temporal "MyString" y no puede vincular un temporal a una referencia a menos que sea "const" (otro en una larga lista de errores de C ++) - Larry

En aras de la integridad, agrego que, además de la conversión de parámetros, el explícito La palabra clave aquí también evitará el uso de la forma de asignación de un ctor de copia (por ejemplo, Foo myFoo = 42;) y requerirá las formas explícitas Foo myFoo = Foo (42); o Foo myFoo (42); - Arbalest

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 personaje 'x' se convertirá implícitamente en int y luego el String(int) se llamará al constructor. Pero esto no es lo que el usuario podría haber querido. 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
};

Respondido 20 Abr '18, 10:04

Y vale la pena señalar que las nuevas reglas de inicialización generalizadas de C ++ 0x harán String s = {0}; mal formado, en lugar de intentar llamar al otro constructor con un puntero nulo, como String s = 0; haría. - Johannes Schaub - litb

Aunque esta es una pregunta antigua, parece que vale la pena señalar algunas cosas (o que alguien me aclare). Al hacer que el formulario int, o ambos ctors, sean 'explícitos', aún tendría el mismo error si usara String mystring('x') cuando quisiste decir String mystring("x") tu no? Además, en el comentario anterior veo el comportamiento mejorado de String s = {0} encima String s = 0 gracias a hacer 'explícita' la forma int del ctor. Pero, aparte de conocer la precedencia de los ctors, ¿cómo sabe la intención (es decir, cómo detectar el error) de este String s{0} ? - Arbalest

@InQusitive: 'x'se trata como un número entero porque el char el tipo de datos es solo un entero de 1 byte. - DavidRR

El problema con tu ejemplo es que solo funciona con copia de inicialización (utilizando =) pero no con inicialización directa (sin uso =): el compilador seguirá llamando al String(int) constructor sin generar un error si escribe String mystring('x');, como señaló @Arbalest. La explicit La palabra clave está destinada a evitar conversiones implícitas que se producen en la inicialización directa y la resolución de funciones. Una mejor solución para su ejemplo sería una simple sobrecarga del constructor: String(char c);. - maggyero

Para evitar confusiones con el argumento char, elimine el constructor char: String(char n) = delete; - mp31415

En C ++, un constructor con un solo parámetro obligatorio se considera una función de conversión implícita. Convierte el tipo de parámetro en el tipo de clase. Si esto es 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 exactamente lo que quieres. Puedes pasar un const char* a una función que espera un String, y el compilador construirá automáticamente un temporal String objeto para ti.

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 desee que el compilador gire silenciosamente ints en Buffers. Para evitar eso, declara 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 un solo parámetro convierte el parámetro en un objeto de su clase, probablemente no desee utilizar el explicit palabra clave. Pero si tiene un constructor que simplemente toma un solo parámetro, debe declararlo como explicit para evitar que el compilador le sorprenda con conversiones inesperadas.

Respondido el 23 de Septiembre de 08 a las 17:09

useBuffer espera un valor l para su argumento, useBuffer(Buffer(4)) tampoco funcionará por eso. Cambiándolo para tomar un const Buffer& or Buffer&& o solo Buffer lo haría funcionar. - pqnet

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

El especificador de función explícita controla las conversiones de tipo implícitas no deseadas. Solo se puede usar en declaraciones de constructores dentro de una declaración de clase. Por ejemplo, a excepción del constructor predeterminado, los constructores de la siguiente clase son constructores de conversión.

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);

Respondido 20 Abr '18, 10:04

"asignar valores"No estamos hablando del operador de asignación aquí. - curioso

Una respuesta de copiar y pegar de una fuente confiable sin referencia alguna aún le otorga a este tipo la friolera de 55 puntos. Nada mal por el "esfuerzo". - trapos2riquezas

reputable source y la copia directa introdujo una noción de explicit A(), aunque esto es solo ruido (el compilador debe ignorarlo), pero no debe estar presente en una respuesta con 55 puntos. - enfermo

Una fuente confiable es incorrecta cuando dice “[explícita] sólo se puede usar en declaraciones de constructores ...” ya que también se puede usar en la declaración de funciones de conversión. - Don Slowik

La palabra clave explicit acompaña ya sea

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

C ++ [class.conv.ctor]

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

2) Un constructor explícito construye objetos al igual que los constructores no explícitos, pero lo hace solo cuando se utiliza explícitamente la sintaxis de inicialización directa (8.5) o cuando se utilizan conversiones (5.2.9, 5.4). 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).

  • o una función de conversión que solo se considera para inicialización directa y 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 sólo se considera 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.

RESUMEN

Las funciones y constructores de conversión explícitos 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 explicit y 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 objeto:

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 objeto:

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é utilizar 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 ambiguo si 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 mediante 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 provocar 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é esperaría de una llamada así? 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 una conversión deseada.

Como dice Bjarne Stroustrup (en "El lenguaje de programación C ++", 4ª edición, 35.2.1, págs. 1011) sobre la cuestión de por qué std::duration no se puede construir implícitamente a partir de un número simple:

Si sabe lo que quiere decir, sea explícito al respecto.

Respondido 12 Jul 15, 12:07

Esta respuesta trata sobre la creación de objetos con / sin un constructor explícito, ya que no se trata 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 formas:

Foo bar1(10);

Foo bar2 = 20;

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

Para los propietarios de Istanbul E-pass el Museo de Madame Tussauds de Estambul es generalmente buena práctica para 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 predeterminados para todos los parámetros, o
  • argumentos predeterminados para el segundo parámetro en adelante

Ambos pueden usarse como constructores de un solo argumento. Por lo que es posible que desee hacer estos también explicit.

Un ejemplo en el que deliberadamente no desea hacer explícito su constructor de un solo argumento si está creando un functor (mire la estructura 'add_x' declarada en este respuesta). En tal caso, crear un objeto como add_x add30 = 30; probablemente tendría sentido.

Aquí es una buena reseña sobre constructores explícitos.

contestado el 23 de mayo de 17 a las 12:05

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

Respondido 14 Feb 13, 16:02

respuesta directa si sabe c ++, si no, puede ir a la respuesta de cjm ... - n611x007

El explicit-la palabra clave se puede utilizar para hacer que se llame a un constructor 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.

El explicit-la palabra clave también se puede utilizar en operadores de conversión de tipos 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 sean válidas las conversiones explícitas, por lo que bool b = c; sería un elenco inválido en este caso. En situaciones como estas explicit-la palabra clave puede ayudar al programador a evitar conversiones implícitas e involuntarias. Este uso se ha estandarizado en C ++ 11.

Respondido 13 Abr '16, 10:04

C c(); en el primer ejemplo no significa lo que crees que significa: es la declaración de una función llamada c que no toma parámetros y devuelve una instancia de C. - 6502

explicit operator bool() es también la versión C ++ 11 de safe bool, y se puede usar implícitamente en verificaciones de condición (y , solamente en controles de estado, hasta donde yo sé). En su segundo ejemplo, esta línea también sería válida en main(): if (c) { std::cout << "'c' is valid." << std:: endl; }. Sin embargo, aparte de esto, no se puede usar sin un casting explícito. - Justin Time - Reincorporar a Monica

"constructor para ser llamado explícitamente" No - curioso

@JustinTime Es una versión estúpida y rota del bool seguro. Toda la idea de conversión implícita explícita es absurda. - curioso

@curiousguy True. Parece un poco un poco tonto, destinado más a ser recordado fácilmente (probablemente con la esperanza de que se traduzca a uso frecuente) que a seguir la lógica en inglés, y está diseñado para no ser totalmente incompatible con implementaciones de bool seguras anteriores (por lo que es menos es probable que rompa algo si lo cambia). OMI, al menos. - Justin Time - Reincorporar a Monica

¡La referencia de Cpp siempre es útil! Se pueden encontrar detalles sobre el especificador explícito aquí. Puede que tenga que mirar conversiones implícitas y copia-inicialización

Vistazo rápido

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 de la siguiente manera:

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
}

Respondido 20 ago 16, 13:08

¡Sí! Estudiar el ejemplo y la descripción de la palabra clave en cppreference es la mejor manera de entender explicit Creo. - dekuArbusto

explicit operator bool() vs if es un caso especial. No hay forma de reproducirlo con definido por el usuario Bool, explicit operator Bool() y una función llamada If. - curioso

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 hace, deseará haberlo hecho ...

Otra buena práctica para las clases es hacer que la construcción de copias y la asignación sean privadas (también conocidas como deshabilitarlas) a menos que realmente necesite implementarlas. Esto evita tener copias eventuales de punteros cuando se utilizan los métodos que C ++ creará para usted de forma predeterminada. Otra forma de hacer esto es derivar de boost::noncopyable.

contestado el 11 de mayo de 20 a las 13:05

Esta publicación está escrita en 2009. Hoy no los declaras como privados, sino que dices = delete. - v010dya

@fmuecke el enlace está muerto, la pregunta fue eliminada - dacabdí

Los constructores agregan 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 dicha especificación, puede utilizar el operador en términos de conversiones explícitas e inicialización directa del objeto.

PD Cuando se utilizan transformaciones definidas POR USUARIO (a través de constructores y operador de conversión de tipo), solo se permite un nivel de conversión implícita. Pero puede combinar estas conversiones con conversiones de otros idiomas.

  • subir rangos integrales (char a int, float a doblar);
  • conversiones estándar (int a doble);
  • convertir punteros de objetos a clase base y void *;

Respondido el 23 de enero de 15 a las 09:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.