Constructeur explicite prenant plusieurs arguments

88

Est-ce que créer un constructeur ayant plusieurs arguments explicita un effet (utile)?

Exemple:

class A {
    public:
        explicit A( int b, int c ); // does explicit have any (useful) effect?
};
Peter G.
la source

Réponses:

120

Jusqu'au C ++ 11, oui, aucune raison d'utiliser explicitsur un constructeur multi-arg.

Cela change dans C ++ 11, en raison des listes d'initialiseurs. Fondamentalement, l'initialisation de copie (mais pas l'initialisation directe) avec une liste d'initialiseurs nécessite que le constructeur ne soit pas marqué explicit.

Exemple:

struct Foo { Foo(int, int); };
struct Bar { explicit Bar(int, int); };

Foo f1(1, 1); // ok
Foo f2 {1, 1}; // ok
Foo f3 = {1, 1}; // ok

Bar b1(1, 1); // ok
Bar b2 {1, 1}; // ok
Bar b3 = {1, 1}; // NOT OKAY
Sneftel
la source
5
Je pense que cette réponse serait meilleure avec "Pourquoi voudrais-je cela" ou "Quand est-ce que cette explication est utile".
MateuszL
La réponse de @MateuszL Edgar donne probablement le meilleur argument pour expliquer pourquoi cela pourrait être utile (et mérite sans doute la coche). La raison pour laquelle il est , cependant, est simplement parce que c'est l'extension logique de la sémantique existante pour explicit. Je ne prendrais pas personnellement la peine de créer des constructeurs multi-arg explicit.
Sneftel
31

Vous tomberiez dessus pour l'initialisation d'accolades (par exemple dans les tableaux)

struct A {
        explicit A( int b, int c ) {}
};

struct B {
         B( int b, int c ) {}
};

int main() {
    B b[] = {{1,2}, {3,5}}; // OK

    A a1[] = {A{1,2}, A{3,4}}; // OK

    A a2[] = {{1,2}, {3,4}}; // Error

    return 0;
}
Conteur - Unslander Monica
la source
24

Les excellentes réponses de @StoryTeller et @Sneftel en sont la principale raison. Cependant, à mon humble avis, cela a du sens (du moins je le fais), dans le cadre de la vérification future des modifications ultérieures du code. Considérez votre exemple:

class A {
    public:
        explicit A( int b, int c ); 
};

Ce code ne bénéficie pas directement de explicit.

Quelque temps plus tard, vous décidez d'ajouter une valeur par défaut pour c, donc cela devient ceci:

class A {
    public:
        A( int b, int c=0 ); 
};

En faisant cela, vous vous concentrez sur le cparamètre - rétrospectivement, il devrait avoir une valeur par défaut. Vous ne vous demandez pas nécessairement si Aelle-même doit être implicitement construite. Malheureusement, ce changement rend à explicitnouveau pertinent.

Donc, pour faire comprendre qu'un ctor est explicit, il pourrait être payant de le faire lors de la première écriture de la méthode.

Ami Tavory
la source
Mais qu'en est-il du cas où le responsable ajoute cette valeur par défaut et conclut que le résultat doit être disponible en tant que constructeur de conversion? Maintenant, ils doivent supprimer ce explicitqui existe depuis toujours, et le support technique sera inondé d'appels à propos de ce changement et passera des heures à expliquer que ce explicitn'était que du bruit et que le supprimer est inoffensif. Personnellement, je ne suis pas très doué pour prédire l'avenir; il est assez difficile de décider quelle interface doit ressembler maintenant .
Pete Becker
@PeteBecker C'est un bon point. Personnellement, je pense que les deux cas sont asymétriques, et qu'il est beaucoup plus courant de rendre les paramètres par défaut (ou de les supprimer) de rendre par inadvertance la classe implicitement constructible, puis de se rendre compte en même temps que rétrospectivement, cela aurait dû l'être. Cela étant dit, ce sont des considérations «douces», et peuvent varier selon les personnes / projets / etc., ou même simplement être une question de goût.
Ami Tavory
8

Voici mes cinq cents à cette discussion:

struct Foo {
    Foo(int, double) {}
};

struct Bar {
    explicit Bar(int, double) {}
};

void foo(const Foo&) {}
void bar(const Bar&) {}

int main(int argc, char * argv[]) {
    foo({ 42, 42.42 }); // valid
    bar({ 42, 42.42 }); // invalid
    return 0;
}

Comme vous pouvez facilement le voir, explicitempêche d'utiliser la liste d'initialisation avec la barfonction bac struct Barcar le constructeur de est déclaré comme explicit.

Edgar Rokjān
la source