Que signifie le mot clé explicit veux dire?

que signifie le mot-clé explicit en C++?

2422
demandé sur Ronny Brendel 2008-09-23 17:58:45

11 réponses

, Le compilateur est autorisé à faire une conversion implicite pour régler les paramètres d'une fonction. Cela signifie que le compilateur peut utiliser des constructeurs appelant avec un monomère pour passer d'un type à un autre afin d'obtenir le bon type pour un paramètre.

voici un exemple de classe avec un constructeur qui peut être utilisé pour les conversions implicites:

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

voici un simple fonction qui prend un Foo objet:

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

et voici où la fonction DoBar est appelée.

int main ()
{
  DoBar (42);
}

L'argument n'est pas un Foo objet, mais un int . Cependant, il existe un constructeur pour Foo qui prend un int de sorte que ce constructeur peut être utilisé pour convertir le paramètre au type correct.

le compilateur est autorisé à faire ceci une fois pour chaque paramètre.

préfixant le mot-clé explicit au constructeur empêche le compilateur d'utiliser ce constructeur pour les conversions implicites. L'ajouter à la classe ci-dessus créera une erreur de compilation à l'appel de fonction DoBar (42) . Il est maintenant nécessaire d'appeler explicitement à la conversion avec DoBar (Foo (42))

la raison pour laquelle vous pourriez vouloir faire ceci est d'éviter la construction accidentelle qui peut cacher des bogues. Exemple artificiel:

  • vous avez une classe MyString(int size) avec un constructeur qui construit une chaîne de la taille donnée. Vous avez une fonction print(const MyString&) , et vous appelez print(3) (quand vous en fait destiné à appeler print("3") ). Vous vous attendez à ce qu'il imprime "3", mais il imprime une chaîne vide de la longueur 3 à la place.
2823
répondu Skizz 2017-06-24 18:46:20

supposons que vous ayez une classe String :

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

maintenant, si vous essayez:

String mystring = 'x';

le caractère 'x' sera implicitement converti en int et alors le constructeur String(int) sera appelé. Mais, ce n'est pas ce que l'utilisateur pourrait avoir prévu. Ainsi, pour éviter de telles conditions, nous définirons le constructeur comme explicit :

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};
982
répondu Eddie 2018-04-20 09:28:46

dans C++, Un constructeur avec un seul paramètre requis est considéré comme une fonction de conversion implicite. Il convertit le type de paramètre en type de classe. Si c'est une bonne chose ou pas dépend de la sémantique du constructeur.

par exemple, si vous avez une classe de corde avec le constructeur String(const char* s) , c'est probablement exactement ce que vous voulez. Vous pouvez passer un const char* à une fonction qui attend un String , et le compilateur sera automatiquement construisez un objet temporaire String pour vous.

d'un autre côté, si vous avez une classe de buffer dont le constructeur Buffer(int size) prend la taille du buffer en octets, vous ne voulez probablement pas que le compilateur transforme tranquillement int s en Buffer s. Pour empêcher cela, vous déclarez le constructeur avec le mot-clé explicit :

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

par là,

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

devient une erreur de compilation. Si vous voulez passer un objet temporaire Buffer , vous devez le faire explicitement:

useBuffer(Buffer(4));

en résumé, si votre constructeur à un seul paramètre convertit le paramètre en un objet de votre classe, vous ne voulez probablement pas utiliser le mot-clé explicit . Mais si vous avez un constructeur qui prendre qu'un seul paramètre, vous devez le déclarer comme explicit pour empêcher le compilateur de vous surprendre avec des conversions inattendues.

136
répondu cjm 2008-09-23 16:37:12

cette réponse concerne la création d'objet avec/sans un constructeur explicite puisqu'elle n'est pas couverte dans les autres réponses.

considérer la classe suivante sans un constructeur explicite:

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

les objets de la classe Foo peuvent être créés de 2 façons:

Foo bar1(10);

Foo bar2 = 20;

selon l'implémentation, la seconde façon d'instancier la classe Foo peut prêter à confusion, ou pas ce que le programmeur avait prévu. La préfixation le mot-clé explicit du constructeur générerait une erreur de compilateur à Foo bar2 = 20; .

Il est habituellement bonnes pratiques de déclarer seul argument des constructeurs comme explicit , à moins que votre mise en œuvre spécifiquement interdit.

noter également que les constructeurs avec

  • arguments par défaut pour tous les paramètres, ou
  • arguments par défaut pour le second paramètre à partir de

peuvent tous les deux être utilisés comme des constructeurs à argument unique. Vous pouvez donc les faire aussi explicit .

un exemple où vous voulez délibérément pas pour rendre votre constructeur à un seul argument explicite est si vous créez un functor (regardez la structure 'add_x' déclarée dans cette réponse ). Dans un tel cas, créer un objet comme add_x add30 = 30; aurait probablement un sens.

ici est une bonne écriture sur les constructeurs explicites.

36
répondu Gautam 2017-05-23 11:47:32

le mot-clé explicit fait un constructeur de conversion en constructeur de non-conversion. Par conséquent, le code est moins sujet aux erreurs.

33
répondu SankararaoMajji 2013-02-14 16:40:06

le mot-clé explicit accompagne soit

  • un constructeur de la classe X qui ne peut pas être utilisé pour convertir implicitement le premier (tout seul) paramètre en type X

C++ [class.conv.ctor]

1) un constructeur déclaré sans spécificateur de fonction explicite spécifie une conversion des types de ses paramètres au type de sa classe. Un tel constructeur est appelé un constructeur de conversion.

2) un constructeur explicite construit des objets tout comme des constructeurs non explicites, mais ne le fait que lorsque la syntaxe d'initialisation directe (8.5) ou les casts (5.2.9, 5.4) sont explicitement utilisés. Un constructeur par défaut peut être un constructeur explicite; un tel constructeur sera utilisé pour effectuer l'initialisation par défaut ou l'initialisation valueinitialization (8.5).

  • ou une fonction de conversion qui n'est considérée que pour l'initialisation directe et la conversion explicite.

C++ [class.conv.fct]

2) une fonction de conversion peut être explicite (7.1.2), auquel cas elle est seulement considérée comme une conversion définie par l'utilisateur pour l'initialisation directe (8.5). Autrement, les conversions définies par l'utilisateur ne sont pas limités à l'utilisation dans les affectations et les initialisations.

vue d'ensemble

les fonctions de conversion explicites et les constructeurs ne peuvent être utilisés que pour les conversions explicites (initialisation directe ou opération de coulée explicite) tandis que les constructeurs non explicites et les fonctions de conversion peuvent être utilisés pour les conversions implicites aussi bien qu'explicites.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

exemple utilisant des structures X, Y, Z et des fonctions foo, bar, baz :

regardons une petite configuration de structures et de fonctions pour voir la différence entre explicit et non - explicit conversions.

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) { }

exemples concernant le constructeur:

Conversion d'un argument de fonction:

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

initialisation de L'objet:

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

exemples concernant les fonctions de conversion:

X x1{ 0 };
Y y1{ 0 };

Conversion d'un argument de fonction:

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

initialisation de L'objet:

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

Pourquoi utiliser explicit fonctions de conversion ou les constructeurs?

les constructeurs de Conversion et les fonctions de conversion Non explicites peuvent introduire une ambiguïté.

considère une structure V , convertible en int , une structure U implicitement constructible de V et une fonction f surchargée pour U et bool respectivement.

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

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

un appel à f est ambigu si l'on passe un objet de type V .

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

le compilateur ne sait pas si utiliser le constructeur de U ou la fonction de conversion pour convertir l'objet V en un type pour passer à f .

Si soit le constructeur de U soit la fonction de conversion de V serait explicit , il n'y aurait aucune ambiguïté puisque seule la conversion non explicite serait considérée. Si les deux sont explicites, l'appel à f en utilisant un objet de type V devrait être fait en utilisant une conversion explicite ou une opération de coulée.

Conversion les constructeurs et les non-conversion explicite fonctions peut entraîner un comportement inattendu.

Considérons une fonction d'impression d'un vecteur:

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

Si la taille-constructeur du vecteur ne serait pas explicite, il serait possible d'appeler la fonction comme ceci:

print_intvector(3);

que peut-on attendre d'un tel appel? Une ligne contenant 3 ou trois lignes contenant 0 ? (Où la seconde est ce qui se passe.)

L'Utilisation du mot-clé explicite dans une interface de classe renforce l'utilisateur de l'interface d'être explicite sur une conversion désirée.

comme le dit Bjarne Stroustrup (dans" The C++ Programming Language", 4e éd., 35.2.1, p. 1011) sur la question de savoir pourquoi std::duration ne peut pas être implicitement construit à partir d'un numéro simple:

si vous savez ce que vous voulez dire, Soyez explicite à ce sujet.

33
répondu Pixelchemist 2015-07-12 11:29:40

le explicit - mot-clé peut être utilisé pour forcer un constructeur à être appelé explicitement .

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

le explicit -mot-clé devant le constructeur C(void) indique au compilateur que seul appel explicite à ce constructeur est autorisé.

le explicit - mot-clé peut également être utilisé dans les opérateurs de fonte de type défini par l'utilisateur:

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

ici, explicit - mot clé applique seulement les moulages explicites pour être valide, donc bool b = c; serait un moulage invalide dans ce cas. Dans des situations comme celles-ci explicit - mot-clé peut aider programmeur à éviter implicite, casts involontaires. Cet usage a été standardisé dans C++11 .

25
répondu Helixirr 2016-04-13 09:08:04

Cela a déjà été discuté ( ce qui est explicite constructeur ). Mais je dois dire qu'il n'y a pas les descriptions détaillées ici.

en outre, il est toujours une bonne pratique de codage de faire vos constructeurs d'un argument (y compris ceux avec des valeurs par défaut pour arg2,arg3,... ainsi que l'a déjà indiqué. Comme toujours avec C++: si vous ne le faites pas, vous le souhaiterez...

une autre bonne pratique pour les classes est de faire une copie construction et cession privé(A. K. A. disable it) à moins que vous ayez vraiment besoin de le mettre en œuvre. Cela évite d'avoir des copies éventuelles de pointeurs lorsque vous utilisez les méthodes que C++ va créer pour vous par défaut. Une autre façon de faire est de dériver de boost::noncopyable.

17
répondu fmuecke 2017-05-23 12:18:28

la référence au RPC est toujours utile!!! Des détails sur le spécificateur explicite peuvent être trouvés ici . Vous pouvez avoir besoin de regarder conversions implicites et copier-initialisation .

coup d'oeil

le spécificateur explicite spécifie qu'un constructeur ou une fonction de conversion (depuis C++11) n'autorise pas les conversions implicites ou la copie-initialisation.

exemple comme suit:

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
répondu selfboot 2016-08-20 12:45:16

conversion Explicite de constructeurs (C++ uniquement)

le spécificateur de fonction explicite contrôle le type implicite indésirable conversion. Il ne peut être utilisé dans les déclarations des constructeurs dans une déclaration de classe. Par exemple, sauf pour la valeur par défaut constructeur, les constructeurs dans la classe suivante sont de conversion constructeur.

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

Les déclarations suivantes juridique:

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

la première déclaration est équivalente à A c = A( 1 ); .

si vous déclarez le constructeur de la classe comme explicit , les déclarations précédentes seraient illégales.

Par exemple, si vous déclarez la classe:

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

vous ne pouvez assigner que des valeurs qui correspondent aux valeurs du type de classe.

par exemple, les déclarations suivantes sont légales:

  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
répondu coding_ninza 2018-04-20 09:32:49

Constructeurs ajouter de la conversion implicite. Pour supprimer cette conversion implicite, il est nécessaire de déclarer un constructeur avec un paramètre explicite.

en C++11 Vous pouvez également spécifier un "type d'opérateur ()" avec un tel mot-clé http://en.cppreference.com/w/cpp/language/explicit avec une telle spécification, vous pouvez utiliser l'opérateur en termes de conversions explicites, et l'initialisation directe de l'objet.

P. S. lors de l'utilisation transformations définies par L'utilisateur (via les constructeurs et l'opérateur de conversion de type) il est permis un seul niveau de conversions implicites utilisées. Mais vous pouvez combiner ces conversions avec d'autres conversions linguistiques

  • jusqu'intégrale rangs (char, int, float double);
  • standart conversions (int, double);
  • convertir les pointeurs d'objets de la classe de base et à void*;
4
répondu bruziuz 2015-01-23 09:26:52