Das explicit-Dilemma
-
Guten Abend,
Das hier in diesem Thread behandelte Problem ist mir schon vor einer Weile aufgefallen, ich bin aber nie dazu gekommen es einmal aufzuschreiben.
In C++03 wurde das
explicit
-Schlüsselwort bei Konstruktoren die mit einem Argument aufgerufen werden konnten benutzt.
Das hat auch wunderbar funktioniert. Der Grund, warum Konstruktoren mit mehreren Parametern nicht mit diesem Specifier deklariert wurden, ist der, dass man sie gar nicht implizit aufrufen konnte.Das hat sich mit C++11 dank copy-list-initialization bekanntlich geändert.
struct Foo { Foo(int, int) {} }; void bar( Foo ){} void bar2( std::vector<Foo> ){} int main() { bar( {1, 2} ); // [...] bar2( {std::begin(a), std::end(a)} ); }
Wer nun behauptet, dass man einen vector-Funktionsparameter so initialisieren dürfen sollte, hat sie m.M.n. nicht mehr alle. Das ist eigentlich das Hauptproblem dabei.
Und wenn wir sagen, "Der Programmierer ist ganz für den Stil verantwortlich", dann sind wir Inkonsequent bezüglich
explicit
- dass ja schon so einige Konstruktoren verpasst bekommen haben.Ein anderes Problem besteht nun darin, dass die Anzahl der Konstruktoren die man mit einem
explicit
-Keyword versehen müsste, sehr groß ist.
Vielleicht sogar größer als die Anzahl der Konstruktoren, die implizit aufrufbar sein sollten.Dann markieren wir zwei von drei Konstruktoren explizit... Redundanz pur. Und alte Klassen müsste man alle updaten, oder den Fakt hinnehmen, dass man ihre Konstruktoren mit einer initializer-list aufrufen kann.
Und alle Konstruktoren standardmäßig explizit zu machen, und ein Schlüsselwort
implicit
einzuführen, ist - dank Inkompabilität mit "älterem" Code - völlig sinnlos und obendrein hässlich.Eine Lösung, die natürlich sofort ins Auge springt, ist die, dass Konstruktoren mit mehreren Parametern, von denen mindestens zwei keine default-Argumente haben, standardmäßig
explicit
sind.
Das wäre auch völlig Kompatibel im Bezug auf initializer_list-Konstruktoren:vector<int> v = {1, 2, 3}; // Geht - initializer_list-Konstruktor hat nur einen Parameter.
Falls das tatsächlich irgendwie nachvollziehbar ist, dann könnte ich oder jemand sonst vielleicht ein Proposal für C++17 schreiben...
Was für Gedanken habt ihr dazu?
Falls irgendwie der Eindruck entsteht, ich mache viel Wind um nichts, sofort sagen. Ich habe nur das Gefühl, mich hat das nicht so richtig in Ruhe gelassen. Wollte mal nachfragen.
~Edit³: Ich wusste, ich hätte die Rechtschreibung von Dilemma prüfen sollten...~
-
dass Konstruktoren mit mehreren Parametern, von denen mindestens zwei keine default-Argumente haben, standardmäßig explicit sind.
Was ich hier nicht berücksichtigt habe, ist, dass man möglicherweise auch einen impliziten, mehrere Parameter habenden Konstruktor wollen könnte... Schlüsselwort
implicit
?
Oder führt einexplicit
in der Deklaration eines solchen Konstruktors zu einem impliziten Konstruktor?
Nein, das ist hässlich.Oder sollte die Sprache implizite Aufrufe von Konstruktoren mit mehreren Parametern komplett verbieten? Hört sich eigentlich sinnvoll an.
-
Sone schrieb:
Wer nun behauptet, dass man einen vector-Funktionsparameter so initialisieren dürfen sollte, hat sie m.M.n. nicht mehr alle. Das ist eigentlich das Hauptproblem dabei.
Deine Meinung ist kein Problem für mich. Ich sehe auch kein anderes.
-
camper schrieb:
Deine Meinung ist kein Problem für mich.
Niemandes Meinung kann ein Problem sein. Meinung ist Meinung und nicht Fakt.
Gut Begründete Meinungen hingegen können einen zum Nachdenken bringen. Leider ist meine nicht sehr gut begründet...
Ich sehe auch kein anderes.
Gut, mal sehen was die anderen sagen. Wahrscheinlich dasselbe. Aber ich wollte einfach mal sehen, ob ich damit der einzige bin oder nicht.
-
Dann markieren wir zwei von drei Konstruktoren explizit... Redundanz pur
Nach längerem Nachdenken erscheint das Argument kraftlos.
Gut, das ist tatsächlich eine dumme Sache. Habe nicht genug darüber Nachgedacht.
-
Sone schrieb:
Wer nun behauptet, dass man einen vector-Funktionsparameter so initialisieren dürfen sollte, hat sie m.M.n. nicht mehr alle.
Noe, du hast sie nicht mehr alle. {} ist ganz klar eine Objektkonstruktion.
Die folgenden beiden Beispiele sollten funktionieren:std::unique_ptr<Foo> makeFoo() { return { new Foo }; }
void foo(std::tuple<int, double, std::string> t); foo({ 42, 3.14, "blubb" });
Ich glaube, viel expliziter geht es gar nicht mehr.
-
std::unique_ptr<Foo> makeFoo() { return { new Foo }; }
Das kompiliert erstens nicht.. zweitens reden wir (ich) von Konstruktoren mit mehreren Parametern. Hast du meinen Post gelesen?
foo({ 42, 3.14, "blubb" });
So, und jetzt ohne Funktionsdeklaration. Wie explizit ist es jetzt?
-
Danke, dass du mich bestaetigst.
-
Für mich ändert sich nichts, ich markiere von jeher alle Konstruktoren als
explicit
, ausgenommen welche die implizit konvertieren können sollen.
Unabhängig davon wie viele Parameter ohne Default-Wert sie haben.
-
Was ändert sich genau mit C++11 im Bezug auf
explicit
?Mir fehlt gerade das Hintergrundwissen -- verhindert das
explicit
-Schlüsselwort eine Konstruktion mittels{}
-Syntax?
-
Nexus schrieb:
Was ändert sich genau mit C++11 im Bezug auf
explicit
?Lies meinen OP.
Mir fehlt gerade das Hintergrundwissen -- verhindert das
explicit
-Schlüsselwort eine Konstruktion mittels{}
-Syntax?Ja, ganz genau.
Aber nicht direct-list-initialization, das ist ja explizit. Sondern copy-list-initialization.Aber im OP habe ich das doch erklärt.
hustbaer schrieb:
Für mich ändert sich nichts, ich markiere von jeher alle Konstruktoren als
explicit
, ausgenommen welche die implizit konvertieren können sollen.
Unabhängig davon wie viele Parameter ohne Default-Wert sie haben.Ja, das ist die Idee.
-
Kellerautomat schrieb:
void foo(std::tuple<int, double, std::string> t); foo({ 42, 3.14, "blubb" });
Ich glaube, viel expliziter geht es gar nicht mehr.
Es ist gar nicht explizit, da an der Stelle wo konstruiert wird der Typ nicht erwähnt wird. Ohne die Signatur der Funktion zu kennen kann man unmöglich sagen was hier konstruiert wird - könnte alles sein was mit diesen Ctor-Arguments klarkommt.
Explizit wäre
void foo(std::tuple<int, double, std::string> t); foo(std::tuple<int, double, std::string>{ 42, 3.14, "blubb" });