Se você perguntar a um programador C ++ sobre o significado da palavra-chave explícita, a maioria responderá que essa palavra-chave é colocada antes da declaração do construtor com um parâmetro (ou com um grande número de parâmetros, mas quando todos os parâmetros, a partir do segundo, têm valores padrão) e impedem a conversão implícita tipos na inicialização.
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; }
No bom e velho C ++ 03, os cenários de aplicação de palavras-chave terminavam aí, no entanto, começando com C ++ 11, o escopo explícito expandido: agora faz sentido não apenas em construtores com um parâmetro, e mesmo não apenas em construtores.
Em 2011, ao Standard foi adicionada a inicialização universal, que deve limpar os métodos de zoológico de inicializar objetos herdados de C ++ de C. Não falarei detalhadamente sobre inicialização universal aqui; existem muitos artigos detalhados sobre esse assunto, seus fácil de encontrar por palavras-chave. Em poucas palavras: propõe-se que os objetos sejam inicializados usando chaves, na verdade essa extensão é a chamada inicialização agregada herdada de C.
Com o advento da inicialização universal, explícito fazia sentido para designers com 0.2.3 ou mais 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; }
Além disso, a partir do C ++ 11, a palavra-chave explícita também pode ser aplicada aos operadores de conversão de tipo, proibindo também a chamada 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; }
Concluindo, eu gostaria de recomendar o uso da inicialização universal em qualquer novo código C ++, além de declarar explicitamente sempre os construtores explícitos, a menos que a conversão implícita seja semanticamente justificada.