Si vous demandez à un programmeur C ++ la signification du mot clé explicite, la plupart répondront que ce mot clé est placé avant la déclaration du constructeur avec un paramètre (ou avec un grand nombre de paramètres, mais lorsque tous les paramètres, à partir du second, ont des valeurs par défaut) et empêche la conversion implicite types lors de l'initialisation.
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; }
Dans le bon vieux C ++ 03, les scénarios d'application des mots-clés s'arrêtaient là, cependant, à partir de C ++ 11, la portée de l'explicite explicite: maintenant, cela a du sens non seulement dans les constructeurs avec un paramètre, et même pas seulement dans les constructeurs.
En 2011, la norme a été ajoutée à l'initialisation universelle, ce qui devrait nettoyer les méthodes du zoo pour initialiser les objets hérités du C ++ de C. Je ne parlerai pas en détail de l'initialisation universelle ici, il existe de nombreux articles détaillés sur ce sujet, leurs facile à trouver par mots clés. En un mot: il est proposé d'initialiser des objets à l'aide d'accolades, en fait, cette extension est la soi-disant initialisation agrégée héritée de C.
Avec l'avènement de l'initialisation universelle, l'explicite a fait sens pour les concepteurs avec 0,2.3 ou plus de paramètres:
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; }
De plus, à partir de C ++ 11, le mot clé explicite peut également être appliqué aux opérateurs de conversion de type, interdisant également leur appel implicite:
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 conclusion, je voudrais recommander d'utiliser l'initialisation universelle dans tout nouveau code C ++, ainsi que de déclarer explicitement toujours des constructeurs explicites, à moins que la conversion implicite ne soit sémantiquement justifiée.