Expliziter Konstruktor mit mehreren Argumenten

87

Hat das Erstellen eines Konstruktors mit mehreren Argumenten expliciteinen (nützlichen) Effekt?

Beispiel:

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

Antworten:

116

Bis C ++ 11 gibt es keinen Grund, expliciteinen Konstruktor mit mehreren Argumenten zu verwenden.

Das ändert sich in C ++ 11 aufgrund von Initialisierungslisten. Grundsätzlich erfordert die Kopierinitialisierung (aber keine direkte Initialisierung) mit einer Initialisierungsliste, dass der Konstruktor nicht markiert ist explicit.

Beispiel:

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
quelle
5
Ich denke, diese Antwort wäre besser mit der Erklärung "Warum sollte ich das wollen" oder "Wann ist das nützlich?".
MateuszL
@MateuszL Edgars Antwort liefert wahrscheinlich das beste Argument dafür, warum es nützlich sein könnte (und verdient wohl das Häkchen). Der Grund ist es dort aber ist , nur weil es die logische Erweiterung der bestehenden Semantik ist explicit. Ich persönlich würde mir nicht die Mühe machen, Konstruktoren mit mehreren Argumenten herzustellen explicit.
Sneftel
31

Sie würden bei der Klammerinitialisierung darauf stoßen (zum Beispiel in Arrays).

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;
}
Geschichtenerzähler - Unslander Monica
quelle
24

Die hervorragenden Antworten von @StoryTeller und @Sneftel sind der Hauptgrund. IMHO, dies ist jedoch sinnvoll (zumindest mache ich das), um spätere Änderungen am Code zu überprüfen. Betrachten Sie Ihr Beispiel:

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

Dieser Code profitiert nicht direkt davon explicit.

Einige Zeit später beschließen Sie, einen Standardwert für hinzuzufügen c, sodass dieser wie folgt lautet:

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

Dabei konzentrieren Sie sich auf den cParameter - im Nachhinein sollte er einen Standardwert haben. Sie konzentrieren Asich nicht unbedingt darauf, ob selbst implizit konstruiert werden soll. Leider macht diese Änderung wieder explicitrelevant.

Um zu vermitteln, dass es sich um einen Ctor handelt explicit, kann es sich lohnen, dies beim ersten Schreiben der Methode zu tun.

Ami Tavory
quelle
Aber was ist mit dem Fall, wenn der Betreuer diesen Standard hinzufügt und zu dem Schluss kommt, dass das Ergebnis als konvertierender Konstruktor verfügbar sein sollte ? Jetzt müssen sie entfernen , explicitdass das schon immer da war, und der technische Support wird mit Anrufen über diese Änderung überschwemmt sein und Stunden damit verbringen, zu erklären, dass dies explicitnur Lärm war und dass das Entfernen harmlos ist. Persönlich bin ich nicht sehr gut darin, die Zukunft vorherzusagen; Es ist schwer genug zu entscheiden, wie eine Benutzeroberfläche jetzt aussehen soll .
Pete Becker
@ PeteBecker Das ist ein guter Punkt. Ich persönlich denke, dass die beiden Fälle asymmetrisch sind und dass es viel häufiger vorkommt, wenn Parameter standardmäßig festgelegt (oder entfernt) werden, um die Klasse versehentlich implizit konstruierbar zu machen, und gleichzeitig tatsächlich zu erkennen, dass dies im Nachhinein der Fall sein sollte. Davon abgesehen sind dies "weiche" Überlegungen und können zwischen Personen / Projekten / etc. Variieren oder auch nur Geschmackssache sein.
Ami Tavory
8

Hier sind meine fünf Cent für diese Diskussion:

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

Wie Sie leicht sehen können, wird explicitverhindert , dass die Initialisierungsliste zusammen mit der barFunktion verwendet wird, da der Konstruktor von struct Barals deklariert ist explicit.

Edgar Rokjān
quelle