Was bedeutet die explicit
Schlüsselwort in C++ bedeuten?
Netter Bericht, Sie sollten vielleicht erwähnen, dass Multi-Arg-Ctors mit Standardparametern auch als Single-Arg-Ctor fungieren können, z. B. Object( const char* name=NULL, int otype=0).
Was bedeutet die explicit
Schlüsselwort in C++ bedeuten?
Der Compiler darf eine implizite Umwandlung vornehmen, um die Parameter in eine Funktion aufzulösen. Das bedeutet, dass der Compiler Konstruktoren verwenden kann, die mit einer einziger Parameter um von einem Typ in einen anderen zu konvertieren, um den richtigen Typ für einen Parameter zu erhalten.
Hier ist eine Beispielklasse mit einem Konstruktor, der für implizite Konvertierungen verwendet werden kann:
class Foo
{
private:
int m_foo;
public:
// single parameter constructor, can be used as an implicit conversion
Foo (int foo) : m_foo (foo) {}
int GetFoo () { return m_foo; }
};
Hier ist eine einfache Funktion, die eine Foo
Objekt:
void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}
und hier ist die DoBar
Funktion aufgerufen wird:
int main ()
{
DoBar (42);
}
Das Argument ist kein Foo
Objekt, sondern ein int
. Es gibt jedoch einen Konstruktor für Foo
die eine int
daher kann dieser Konstruktor verwendet werden, um den Parameter in den richtigen Typ zu konvertieren.
Der Compiler darf dies für jeden Parameter einmal tun.
Das Voranstellen der explicit
Schlüsselwort an den Konstruktor verhindert, dass der Compiler diesen Konstruktor für implizite Konvertierungen verwendet. Das Hinzufügen des Schlüsselworts zur obigen Klasse führt zu einem Compilerfehler beim Funktionsaufruf DoBar (42)
. Es ist nun notwendig, die Konvertierung explizit aufzurufen mit DoBar (Foo (42))
Der Grund, warum Sie dies tun sollten, ist, dass Sie versehentliche Konstruktionen vermeiden wollen, in denen sich Wanzen verstecken können.
Ein erfundenes Beispiel:
MyString
Klasse mit einem Konstruktor, der eine Zeichenkette der angegebenen Größe konstruiert. Sie haben eine Funktion print(const MyString&)
(sowie eine Überlastung print (char *string)
), und Sie rufen print(3)
(wenn Sie eigentlich beabsichtigt, anrufen print("3")
). Sie erwarten, dass er "3" ausgibt, aber er gibt stattdessen eine leere Zeichenkette der Länge 3 aus.
Netter Bericht, Sie sollten vielleicht erwähnen, dass Multi-Arg-Ctors mit Standardparametern auch als Single-Arg-Ctor fungieren können, z. B. Object( const char* name=NULL, int otype=0).
Ich denke, es sollte auch erwähnt werden, dass man in Erwägung ziehen sollte, Konstruktoren mit einem Argument zunächst explizit zu machen (mehr oder weniger automatisch) und das explizite Schlüsselwort nur dann zu entfernen, wenn die implizite Konvertierung erwünscht ist konstruktionsbedingt . Ich denke, Konstruktoren sollten standardmäßig explizit sein, mit einem "implicit"-Schlüsselwort, damit sie als implizite Konvertierungen arbeiten können. Aber das ist nicht der Fall.
@thecoshman: Man deklariert nicht eine Parameter explicit
-- Sie deklarieren eine Konstrukteur explicit
. Aber ja: Ihre Parameter vom Typ Foo
müssen konstruiert werden explicite
len, werden sie nicht stillschweigend konstruiert, indem man einfach die Parameter ihres Konstruktors in die Funktion einfügt.
Angenommen, Sie haben eine Klasse String
:
class String {
public:
String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
Wenn Sie es jetzt versuchen:
String mystring = 'x';
Das Zeichen 'x'
wird implizit umgewandelt in int
und dann die String(int)
Konstruktor aufgerufen werden. Dies ist jedoch nicht das, was der Benutzer beabsichtigt haben könnte. Um solche Bedingungen zu vermeiden, definieren wir den Konstruktor als explicit
:
class String {
public:
explicit String (int n); //allocate n bytes
String(const char *p); // initialize sobject with string p
};
Und es ist erwähnenswert, dass die neuen verallgemeinerten Initialisierungsregeln von C++0x die String s = {0};
schlecht geformt, anstatt zu versuchen, den anderen Konstruktor mit einem Null-Zeiger aufzurufen, wie String s = 0;
tun würde.
Auch wenn es sich um eine alte Frage handelt, scheint es sich zu lohnen, auf ein paar Dinge hinzuweisen (oder jemanden zu bitten, mich aufzuklären). Indem man die int-Form oder beide Koren "explizit" macht, würde man immer noch den gleichen Fehler haben, wenn man String mystring('x')
wenn Sie meinten String mystring("x")
Oder etwa nicht? Außerdem entnehme ich dem obigen Kommentar das verbesserte Verhalten von String s = {0}
über String s = 0
dank der 'expliziten' Verwendung der int-Form des ctor. Aber abgesehen davon, dass Sie den Vorrang der Ctors kennen, wie können Sie die Absicht (d.h. wie Sie den Fehler erkennen) dieses String s{0}
?
In C++ wird ein Konstruktor mit nur einem erforderlichen Parameter als implizite Konvertierungsfunktion betrachtet. Sie konvertiert den Parametertyp in den Klassentyp. Ob dies eine gute Sache ist oder nicht, hängt von der Semantik des Konstruktors ab.
Wenn Sie zum Beispiel eine String-Klasse mit dem Konstruktor String(const char* s)
ist das wahrscheinlich genau das, was Sie wollen. Sie können eine const char*
zu einer Funktion, die eine String
und der Compiler konstruiert automatisch eine temporäre String
Objekt für Sie.
Andererseits, wenn Sie eine Pufferklasse haben, deren Konstruktor Buffer(int size)
die Größe des Puffers in Bytes angibt, wollen Sie wahrscheinlich nicht, dass der Compiler stillschweigend in int
s in Buffer
s. Um dies zu verhindern, deklarieren Sie den Konstruktor mit der explicit
Stichwort:
class Buffer { explicit Buffer(int size); ... }
Auf diese Weise,
void useBuffer(Buffer& buf);
useBuffer(4);
wird zu einem Kompilierzeitfehler. Wenn Sie eine temporäre Buffer
Objekt, müssen Sie dies explizit tun:
useBuffer(Buffer(4));
Zusammenfassend lässt sich sagen, dass Sie, wenn Ihr Ein-Parameter-Konstruktor den Parameter in ein Objekt Ihrer Klasse umwandelt, wahrscheinlich nicht die explicit
Stichwort. Wenn Sie jedoch einen Konstruktor haben, der nur einen einzigen Parameter benötigt, sollten Sie ihn als explicit
um zu verhindern, dass der Compiler Sie mit unerwarteten Konvertierungen überrascht.
explicit
begleitet entwederC++ [class.conv.ctor]
1) Ein Konstruktor, der ohne expliziten Funktionsspezifizierer deklariert wird, gibt eine Konvertierung von den Typen seiner Parameter in den Typ seiner Klasse an. Ein solcher Konstruktor wird als konvertierender Konstruktor bezeichnet.
2) Ein expliziter Konstruktor konstruiert Objekte genauso wie nicht-explizite Konstruktoren, tut dies aber nur, wenn die Syntax der direkten Initialisierung (8.5) oder Casts (5.2.9, 5.4) explizit verwendet werden. Ein Standard-Konstruktor kann ein expliziter Konstruktor sein; ein solcher Konstruktor wird verwendet, um eine Standard-Initialisierung oder Wert-Initialisierung durchzuführen (8.5).
C++ [class.conv.fct]
2) Eine Konvertierungsfunktion kann explizit sein (7.1.2); in diesem Fall wird sie nur als benutzerdefinierte Konvertierung für die direkte Initialisierung (8.5) betrachtet. Ansonsten sind benutzerdefinierte Konvertierungen nicht auf die Verwendung in Zuweisungen beschränkt und Initialisierungen beschränkt.
Explizite Konvertierungsfunktionen und Konstruktoren können nur für explizite Konvertierungen verwendet werden (direkte Initialisierung oder explizite Cast-Operation), während nicht-explizite Konstruktoren und Konvertierungsfunktionen sowohl für implizite als auch für explizite Konvertierungen verwendet werden können.
/*
explicit conversion implicit conversion
explicit constructor yes no
constructor yes yes
explicit conversion function yes no
conversion function yes yes
*/
X, Y, Z
und Funktionen foo, bar, baz
:Schauen wir uns einen kleinen Aufbau von Strukturen und Funktionen an, um den Unterschied zwischen explicit
und nicht explicit
Umrechnungen.
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) { }
Umwandlung eines Funktionsarguments:
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
Objekt-Initialisierung:
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
X x1{ 0 };
Y y1{ 0 };
Umwandlung eines Funktionsarguments:
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
Objekt-Initialisierung:
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
explicit
Konvertierungsfunktionen oder Konstruktoren?Konvertierungskonstruktoren und nicht explizite Konvertierungsfunktionen können zu Mehrdeutigkeit führen.
Betrachten Sie eine Struktur V
, umwandelbar in int
eine Struktur U
implizit konstruierbar aus V
und eine Funktion f
überlastet für U
y bool
beziehungsweise.
struct V {
operator bool() const { return true; }
};
struct U { U(V) { } };
void f(U) { }
void f(bool) { }
Ein Aufruf zur f
ist zweideutig, wenn ein Objekt vom Typ V
.
V x;
f(x); // error: call of overloaded 'f(V&)' is ambiguous
Der Compiler weiß nicht, ob er den Konstruktor von U
oder die Konvertierungsfunktion zur Umwandlung der V
Objekt in einen Typ zur Übergabe an f
.
Wenn entweder der Konstruktor von U
oder die Umrechnungsfunktion von V
sería explicit
würde es keine Unklarheiten geben, da nur die nicht explizite Umwandlung berücksichtigt würde. Wenn beide explizit sind, wird der Aufruf von f
mit einem Objekt des Typs V
müsste eine explizite Konvertierung oder ein Casting durchgeführt werden.
Konvertierungskonstruktoren und nicht explizite Konvertierungsfunktionen können zu unerwartetem Verhalten führen.
Betrachten Sie eine Funktion, die einen Vektor druckt:
void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }
Wenn der Größenkonstruktor des Vektors nicht explizit wäre, könnte man die Funktion so aufrufen:
print_intvector(3);
Was würde man von einem solchen Anruf erwarten? Eine Zeile mit 3
oder drei Zeilen mit 0
? (Wobei das zweite das ist, was passiert.)
Wie Bjarne Stroustrup (in "The C++ Programming Language", 4th Ed., 35.2.1, pp. 1011) auf die Frage, warum std::duration
kann nicht stillschweigend aus einer einfachen Zahl konstruiert werden:
Wenn Sie wissen, was Sie meinen, sollten Sie es explizit sagen.
In dieser Antwort geht es um die Erstellung von Objekten mit/ohne expliziten Konstruktor, da dies in den anderen Antworten nicht behandelt wird.
Betrachten Sie die folgende Klasse ohne einen expliziten Konstruktor:
class Foo
{
public:
Foo(int x) : m_x(x)
{
}
private:
int m_x;
};
Objekte der Klasse Foo können auf 2 Arten erstellt werden:
Foo bar1(10);
Foo bar2 = 20;
Je nach Implementierung kann die zweite Art der Instanziierung der Klasse Foo verwirrend sein oder nicht dem Willen des Programmierers entsprechen. Das Voranstellen der explicit
Schlüsselwort in den Konstruktor würde einen Compilerfehler bei Foo bar2 = 20;
.
Sie ist in der Regel gute Praxis, Konstruktoren mit einem Argument als explicit
Es sei denn, Ihre Implementierung verbietet dies ausdrücklich.
Beachten Sie auch, dass Konstruktoren mit
können beide als Ein-Argument-Konstruktoren verwendet werden. Sie können diese also auch als explicit
.
Ein Beispiel, wenn Sie absichtlich no Wenn Sie einen Konstruktor mit einem einzigen Argument explizit machen wollen, müssen Sie einen Funktor erstellen (siehe die Struktur "add_x", die in diese Antwort). In einem solchen Fall ist die Erstellung eines Objekts als add_x add30 = 30;
würde wahrscheinlich Sinn machen.
Hier ist ein guter Artikel über explizite Konstruktoren.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
228 Stimmen
Ich möchte nur jeden, der neu hinzukommt, darauf hinweisen, dass seit C++11,
explicit
kann nicht nur auf Konstruktoren angewendet werden. Es gilt jetzt auch für Konvertierungsoperatoren. Angenommen, Sie haben eine KlasseBigInt
mit einem Umwandlungsoperator inint
und einen expliziten Umwandlungsoperator instd::string
aus welchem Grund auch immer. Sie werden sagen könnenint i = myBigInt;
aber Sie müssen explizit einen Cast durchführen (mitstatic_cast
, vorzugsweise), um zu sagenstd::string s = myBigInt;
.2 Stimmen
Kann man sich nicht auch explizit auf den Auftrag beziehen? (d.h..
int x(5);
)0 Stimmen
@chris Die Idee einer expliziten impliziten Umwandlung ist absurd. Lassen Sie die Finger davon!
4 Stimmen
@curiousguy, Es gibt keine explizite implizite Konvertierung.
0 Stimmen
@chris Es gibt ein explizites Schlüsselwort, das bei der Deklaration einer impliziten Konvertierung verwendet werden kann.
2 Stimmen
@curiousguy, Es ist nicht per se eine implizite Umwandlung. Das Einfügen von
explicit
deklariert dort eine explizite Konvertierung in einen Typ. Keine Selbstverständlichkeit in dem Prozess beteiligt.0 Stimmen
@chris Explizite Umwandlung ist ein schlecht definierter Begriff.
0 Stimmen
@curiousguy: Wie meinen Sie das? Alle Konvertierungen sollten implizit sein? Um alle Arten von stillschweigend angewandten, lustigen Fehlkonvertierungen aufgrund von zufälligen Mehrdeutigkeiten loszulassen? (Siehe z.B. den Abschnitt "Das sichere bool-Problem" unter diese Seite C++ref. o open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2333.html für (viel) mehr Details darüber, warum "Explizite Konvertierung ist ein schlecht definiertes Konzept." eine schlecht durchdachte Aussage ist).
0 Stimmen
@Sz. Ich meine, dass explizite Konvertierung keine Sache ist; es ist ein Müllkonzept. Auch das "safe bool" ist ein lächerlicher "Beweis" für die Nützlichkeit der Idee des "expliziten Operators", da es nicht einmal eine Anwendung dieser Idee ist, sondern ein anderer Satz von Regeln, was bedeutet die einzige praktische Verwendung von "explicit operator" in der SL ist ad hoc und nicht auf UDT anwendbar .
0 Stimmen
@chris meinten Sie so:
std::string s = static_cast<std::string>(myBigInt)
? Wenn möglich, könnten Sie bitte Ihren ersten Kommentar näher erläutern? Vielen Dank im Voraus!2 Stimmen
@Milan, Ja, das ist genau das Richtige. Wenn Sie mehr Informationen suchen, diese Antwort schreibt es förmlicher aus. Bitte beachten Sie, dass
bool
ist in dieser Hinsicht besonders . Diese Antworten und die Suche nach "expliziten Konvertierungsoperatoren" werden Sie zu weiteren Beiträgen über diese Funktion führen und sind besser geeignet als eine Kommentar-Kette.