c++ - wikiversity - valeur unsigned int




Que signifie le mot clé explicite? (8)

Le mot clé explicit accompagne soit

  • un constructeur de classe X qui ne peut pas être utilisé pour convertir implicitement le premier paramètre (n'importe lequel) en type X

C ++ [class.conv.ctor]

1) Un constructeur déclaré sans le spécificateur de fonction explicit spécifie une conversion des types de ses paramètres vers le type de sa classe. Un tel constructeur s'appelle un constructeur de conversion.

2) Un constructeur explicite construit des objets exactement comme des constructeurs non explicites, mais uniquement 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 une initialisation par défaut ou une initialisation de valeur (8.5).

  • ou une fonction de conversion qui n'est prise en compte 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 uniquement considérée comme une conversion définie par l'utilisateur pour une initialisation directe (8.5). Sinon, les conversions définies par l'utilisateur ne sont pas limitées à une utilisation dans les affectations et les initialisations.

Vue d'ensemble

Les fonctions et les constructeurs de conversion explicites ne peuvent être utilisés que pour les conversions explicites (initialisation directe ou opération de conversion explicite), tandis que les constructeurs et les fonctions de conversion non explicites peuvent être utilisés pour les conversions implicites et explicites.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Exemple utilisant les structures X, Y, Z et les fonctions foo, bar, baz :

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

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 d'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 d'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 explicit ou des constructeurs?

Les constructeurs de conversion et les fonctions de conversion non explicites peuvent introduire une ambiguïté.

Considérons une structure V , convertible en int , une structure U implicitement constructible à partir 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 s’il passe un objet de type V

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

Le compilateur ne sait pas s'il doit utiliser le constructeur de U ou la fonction de conversion pour convertir l'objet V en un type à transmettre à f .

Si le constructeur de U ou la fonction de conversion de V serait explicit , il n'y aurait aucune ambiguïté puisque seule la conversion non explicite serait prise en compte. Si les deux sont explicites, l'appel de f aide d'un objet de type V doit être effectué à l'aide d'une conversion explicite ou d'une opération de conversion.

Les constructeurs de conversion et les fonctions de conversion non explicites peuvent entraîner un comportement inattendu.

Considérons une fonction imprimant un vecteur:

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

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

print_intvector(3);

Qu'attendrait-on d'un tel appel? Une ligne contenant 3 ou trois lignes contenant 0 ? (Où le second est ce qui se passe.)

L'utilisation du mot clé explicit dans une interface de classe oblige l'utilisateur de l'interface à être explicite à propos de la conversion souhaitée.

Comme Bjarne Stroustrup le dit (dans "Le langage de programmation C ++", 4e éd., 35.2.1, p. 1011) sur la question de savoir pourquoi std::duration ne peut pas être construit implicitement à partir d'un nombre simple:

Si vous savez ce que vous voulez dire, soyez explicite à ce sujet.

Que signifie le mot clé explicit en C ++?


Cela a déjà été discuté ( quel est le constructeur explicite ). Mais je dois dire qu'il manque les descriptions détaillées trouvées ici.

De plus, il est toujours bon de coder vos constructeurs à un seul argument (y compris ceux avec des valeurs par défaut pour arg2, arg3, ...), comme indiqué précédemment. Comme toujours avec C ++: si vous ne le faites pas, vous souhaiterez le faire ...

Une autre bonne pratique pour les classes est de rendre la construction de la copie et l’affectation privées (autrement dit de les désactiver), sauf si vous avez vraiment besoin de les implémenter. Cela évite d'avoir des copies éventuelles de pointeurs lorsque vous utilisez les méthodes que C ++ créera pour vous par défaut. Une autre façon de faire est d'utiliser boost :: noncopyable.


Cpp Reference est toujours utile !!! Des détails sur le spécificateur explicite peuvent être trouvés here . Vous devrez peut-être également examiner les conversions implicites et l’ copy-initialization .

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 ni l'initialisation de copie.

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
}

En C ++, un constructeur avec un seul paramètre obligatoire est considéré comme une fonction de conversion implicite. Il convertit le type de paramètre en type de classe. Que ce soit une bonne chose ou non dépend de la sémantique du constructeur.

Par exemple, si vous avez une classe de chaîne avec le constructeur String(const char* s) , c'est probablement exactement ce que vous voulez. Vous pouvez transmettre un caractère const char* à une fonction qui attend une String et le compilateur construira automatiquement un objet String temporaire pour vous.

D'autre part, si vous avez une classe de mémoire tampon dont le constructeur Buffer(int size) prend la taille de la mémoire tampon en octets, vous ne voudrez probablement pas que le compilateur transforme silencieusement int s en Buffer . Pour empêcher cela, vous déclarez le constructeur avec le mot-clé explicit :

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

De cette façon,

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

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

useBuffer(Buffer(4));

En résumé, si votre constructeur à paramètre unique convertit le paramètre en objet de votre classe, vous ne souhaiterez probablement pas utiliser le mot clé explicit . Mais si vous avez un constructeur qui prend simplement un paramètre, vous devez le déclarer explicit pour empêcher le compilateur de vous surprendre avec des conversions inattendues.


Le mot-clé explicit crée un constructeur de conversion en constructeur de non-conversion. En conséquence, le code est moins sujet aux erreurs.


Le mot-clé explicit peut être utilisé pour imposer à un constructeur d'être appelé explicitement .

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

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

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

Le mot-clé explicit peut également être utilisé dans les opérateurs de transtypage de types définis 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 -keyword impose que seuls les conversions explicites soient valides, alors bool b = c; serait un casting non valide dans ce cas. Dans des situations comme celles-ci, explicit -keyword peut aider le programmeur à éviter les conversions implicites et involontaires. Cet usage a été normalisé en C++11 .


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 , puis le constructeur String(int) sera appelé. Mais, ce n'est pas ce que l'utilisateur aurait pu vouloir. Donc, pour éviter de telles conditions, nous allons définir le constructeur comme étant explicit :

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

Constructeurs de conversion explicites (C ++ uniquement)

Le spécificateur de fonction explicite contrôle les conversions de type implicites indésirables. Il ne peut être utilisé que dans les déclarations de constructeurs d'une déclaration de classe. Par exemple, à l'exception du constructeur par défaut, les constructeurs de la classe suivante sont des constructeurs de conversion.

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

Les déclarations suivantes sont légales:

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 en tant que:

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

Vous ne pouvez affecter que des valeurs correspondant 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);




explicit-constructor