explícito en detalle

Si le pregunta a un programador de C ++ sobre el significado de la palabra clave explícita, la mayoría responderá que esta palabra clave se coloca antes de la declaración del constructor con un parámetro (o con una gran cantidad de parámetros, pero cuando todos los parámetros, a partir del segundo, tienen valores predeterminados) y evita la conversión implícita tipos tras la inicialización.


class Simple { public: Simple(int a) : a_(a) {} private: int a_; }; class SimpleExplicit { public: explicit SimpleExplicit(int a) : a_(a) {} private: int a_; }; template <typename S> void someFunc(const S& s) { } int main(int, char**) { Simple s3 = 11; // SimpleExplicit se3 = 11; - COMPILE ERROR SimpleExplicit se3 = SimpleExplicit(11); someFunc<Simple>(11); // someFunc<SimpleExplicit>(11); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit(11)); return 0; } 

En el viejo C ++ 03, los escenarios de aplicación de palabras clave terminaban allí, sin embargo, comenzando con C ++ 11, el alcance de explícitamente expandido: ahora tiene sentido no solo en constructores con un parámetro, e incluso no solo en constructores.


En 2011, el Estándar agregó una inicialización uniforme, que debería limpiar los métodos del zoológico para inicializar objetos heredados de C ++. No hablaré en detalle sobre la inicialización universal aquí, hay muchos artículos detallados sobre este tema, sus Fácil de encontrar por palabras clave. En pocas palabras: se propone que los objetos se inicialicen con llaves, de hecho, esta extensión es la llamada inicialización agregada heredada de C.


Con el advenimiento de la inicialización universal, lo explícito tiene sentido para los diseñadores con 0.2.3 o más parámetros:


 class Simple { public: Simple() : a_(0), b_(0) {} Simple(int a) : a_(a), b_(0) {} Simple(int a, int b) : a_(a), b_(b) {} private: int a_, b_; }; class SimpleExplicit { public: explicit SimpleExplicit() : a_(0), b_(0) {} explicit SimpleExplicit(int a) : a_(a), b_(0) {} explicit SimpleExplicit(int a, int b) : a_(a), b_(b) {} private: int a_, b_; }; template <typename S> void someFunc(const S& s) { } int main(int, char**) { Simple s4 = {}; someFunc<Simple>({}); // SimpleExplicit se4 = {}; - COMPILE ERROR SimpleExplicit se4 = SimpleExplicit{}; // someFunc<SimpleExplicit>({}); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit{}); Simple s5 = {11}; someFunc<Simple>({11}); // SimpleExplicit se5 = {11}; - COMPILE ERROR SimpleExplicit se5 = SimpleExplicit{11}; // someFunc<SimpleExplicit>({11}); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit{11}); Simple s6 = {11, 22}; someFunc<Simple>({11, 22}); // SimpleExplicit se6 = {11, 22}; - COMPILE ERROR SimpleExplicit se6 = SimpleExplicit{11, 22}; // someFunc<SimpleExplicit>({11, 22}); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit{11, 22}); return 0; } 

Además, comenzando con C ++ 11, la palabra clave explícita también se puede aplicar a operadores de conversión de tipos, prohibiendo también su invocación implícita:


 class Simple { public: Simple() {} operator bool() const { return true; } }; class SimpleExplicit { public: explicit SimpleExplicit() {} explicit operator bool() const { return true; } }; int main(int, char**) { Simple s7{}; bool b7 = s7; SimpleExplicit se7{}; // bool be7 = se7; - COMPILE ERROR bool be7 = static_cast<bool>(se7); return 0; } 

En conclusión, me gustaría recomendar el uso de la inicialización universal en cualquier código C ++ nuevo, así como declarar explícitamente los constructores explícitos siempre, a menos que la conversión implícita esté semánticamente justificada.

Source: https://habr.com/ru/post/es436296/


All Articles