2016-04-19 8 views
17

Nach here, explicit:Prevent unerwünschte Umwandlung in Konstruktor

Gibt Konstruktoren und Umwandlungsoperatoren (da C++ 11), dass erlauben keine implizite Konvertierungen oder Kopieren-Initialisierung.

Sind also diese beiden Techniken identisch?

struct Z { 
     // ... 
     Z(long long);  // can initialize with a long long 
     Z(long) = delete; // but not anything smaller 
}; 

struct Z { 
     // ... 
     explicit Z(long long);  // can initialize ONLY with a long long 
}; 

Antwort

17

Sie sind nicht identisch.

Z z = 1LL; 

Das obige funktioniert mit der nicht expliziten Version, aber nicht mit der expliziten Version.

Das Deklarieren des Konstruktors Z explizit verhindert nicht die Konvertierung des Konstruktorarguments von einem anderen Typ. Es verhindert die Konvertierung von dem Argument zu Z ohne den Konstruktor explizit aufzurufen.

Unten ist ein Beispiel für expliziten Konstruktoraufruf.

Z z = Z(1LL); 
+3

Beachten Sie, dass das den expliziten Konstruktor und auch den Copy/Move-Konstruktor aufruft. 'Z z (1LL);' würde nur den expliziten Konstruktor aufrufen. – immibis

24

Nein, sie sind nicht gleich. explicit lässt implizite Konvertierungen zu diesem Typ nicht zu, wenn dieser Konstruktor ausgewählt ist - implizite Konvertierungen in Argumenten sind nicht wichtig. delete verbietet jede Konstruktion, wenn dieser Konstruktor ausgewählt ist, und kann dazu verwendet werden, die implizite Umwandlung des Arguments zu verbieten.

So zum Beispiel:

struct X { 
    explicit X(int) { } 
}; 

void foo(X) { } 

foo(4);  // error, because X's constructor is explicit 
foo(X{3}); // ok 
foo(X{'3'}); // ok, this conversion is fine 

, die aus delete ing einen Konstruktor getrennt ist:

struct Y { 
    Y(int) { } 
    Y(char) = delete; 
}; 

void bar(Y) { } 

bar(4);  // ok, implicit conversion to Y since this constructor isn't explicit 
bar('4'); // error, this constructor is deleted 
bar(Y{'4'}); // error, doesn't matter that we're explicit 

Die beiden Techniken auch orthogonal sind. Wenn Sie eine Art nicht sein wollen implizit konvertierbare und nur konstruierbar aus genau einem int, können Sie beides:

struct W { 
    explicit W(int) { } 

    template <class T> 
    W(T) = delete; 
}; 

void quux(W); 

quux(4);  // error, constructor is explicit 
quux('4'); // error, constructor is deleted 
quux(4L);  // error, constructor is deleted 
quux(W{'4'}); // error, constructor is deleted 
quux(W{5}); // ok 
+0

blech, unnötige Verwendung von uniform init. Verwenden Sie direkte Initplix. – Puppy

+2

@Puppy Sie haben mich wegen der Verwendung von Zahnspangen abgelehnt? Ernst? – Barry

+3

@Puppy Btw, 'W {5}' * ist * direkte Initialisierung ... – Barry

2
struct Zb { 
     Zb(long long) 
     {};  // can initialize with a long long 
     Zb(long) = delete; // but not anything smaller 
    }; 

struct Za { 
     // ... 
     explicit Za(long long) 
     {};  // can initialize ONLY with a long long 
    }; 

int main() 
{ 
    Za((long long)10); // works 
    Za((long)10);  // works  

    Zb((long long)10); // works 
    Zb((long)10);  // does not work 

    return 0; 
} 

Ihr Beispiel explizite Löschen erfordert.

Live-: http://cpp.sh/4sqb

1

Sie sind nicht das gleiche.

Aus dem Standard-Arbeitsentwurf n4296:

12.3.1 - [class.conv.ctor]:
Ein Konstruktor deklariert ohne die Funktion-specifier explizit spezifiziert eine Umwandlung von den Typen ihrer Parameter auf die Art seiner Klasse . Ein solcher Konstruktor wird als Konvertierungskonstruktor bezeichnet.

Ein expliziter Konstruktor konstruiert Objekte wie nicht-explizite Bauer, tut dies aber nur, wenn die Direkt Initialisierung Syntax (8.5) oder in denen Abgüsse werden (5.2.9, 5.4) explizit verwendet. Ein Standardkonstruktor kann ein expliziter Konstruktor sein; Ein solcher Konstruktor wird verwendet, um die Standardinitialisierung oder Wertinitialisierung (8.5) durchzuführen.

durch ein Beispiel eines jeden Gefolgt jeweils:

struct X { 
    X(int); 
    X(const char*, int =0); 
    X(int, int); 
}; 

void f(X arg) { 
    X a = 1;  // a = X(1) 
    X b = "Jessie"; // b = X("Jessie",0) 
    a = 2;   // a = X(2) 
    f(3);   // f(X(3)) 
    f({1, 2});  // f(X(1,2)) 
} 

Mit explizitem Konstruktor:

struct Z { 
    explicit Z(); 
    explicit Z(int); 
    explicit Z(int, int); 
}; 

Z a;      // OK: default-initialization performed 
Z a1 = 1;     // error: no implicit conversion 
Z a3 = Z(1);    // OK: direct initialization syntax used 
Z a2(1);     // OK: direct initialization syntax used 
Z* p = new Z(1);   // OK: direct initialization syntax used 
Z a4 = (Z)1;    // OK: explicit cast used 
Z a5 = static_cast<Z>(1); // OK: explicit cast used 
Z a6 = { 3, 4 };   // error: no implicit conversion 
5

explicit Blöcke implizite Konvertierung zu Ihrem Typ.

Ihre =delete Technik blockiert die implizite Konvertierung von long zu long long.

Diese sind fast nicht miteinander verwandt.

Es gibt 4 Fälle, die den Unterschied veranschaulichen:

Z z = 1L; 
Z z = 1LL; 

ist eine implizite Konvertierung von long und long long zu Z.

Z z = Z(1L); 
Z z = Z(1LL); 

ist eine explizite Umwandlung von long und long long zu Z.

explicit Z(long long) Blöcke:

Z z = 1L; 
Z z = 1LL; 

während Z(long)=delete Blöcke:

Z z = 1L; 
Z z = Z(1L); 

explicit Z(long long) ermöglicht Z z = Z(1L), da die Umwandlung long-long long implizit, aber in keinem Zusammenhang mit der expliziten Umwandlung in Z, die danach geschieht.

Beachten Sie, dass eine Mischung aus explicit und =delete nur Z z=Z(1LL) als gültig unter Ihren 4 Versionen übrig lässt.

(das oben genannte setzt eine gültige Kopie voraus oder bewegt ctor; wenn nicht, ersetzen Sie Z z=Z(...) durch Z z(...) und die gleichen Schlussfolgerungen ergeben).