C++11 (aka C++0x) - Approval of Final Committee Draft



  • audacia schrieb:

    Nexus schrieb:

    Wie stellst du sicher, dass der Anwender nicht in seinem Code Funktionen definiert, diese als privat bezeichnet und somit vollständigen Zugriff auf Klasseninterna erhält?

    Da sie privat sind, kann er sie nicht aufrufen, also nutzt es ihm nichts.

    // MyClass.hpp
    
    class MyClass
    {
    public:
      void foo(char c) { fooPrivate(c); }
      // Ist sowas dann noch möglich? fooPrivate wäre nicht bekannt!
    };
    
    // MyClass.cpp
    
    private void MyClass::fooPrivate(int val)
    {
      // ...
    }
    
    // EvilUser.cpp
    
    private void MyClass::fooPrivate(char c)
    {
      cout << "HrHrHr" << endl;
    }
    

    😉



  • Beispiel schrieb:

    class MyClass
    {
    public:
      void foo(char c) { fooPrivate(c); }
      // Ist sowas dann noch möglich? fooPrivate wäre nicht bekannt!
    

    Was im Header nicht bekannt ist, kannst du natürlich auch nicht referenzieren. Wie sollte der Compiler diese Funktion auch inline expandieren, wenn er fooPrivate() nicht kennt.

    Mit Templates und einem Compiler, der "two-phase name lookup" nicht implementiert, kannst du allerdings eine eine derartige Situation konstruieren. Eine mögliche Gegenmaßnahme wäre, implizite Überladungen auf diesem Wege zu verbieten. Aber da wird dieses ach so einfache Feature natürlich schon wieder kompliziert.



  • Dein Ton lässt echt zu wünschen übrig, Ben04. Ich muss mir Deinen Überlegenheitskomplex nicht gefallen lassen.

    ... Ich hoffe ich brauche nicht erklären ...

    Spar Dir die Mühe. Wir sind alle dumm und Du nicht, richtig?

    Siehst du, dass du auch von selbst drauf kommst, wenn du es nur versuchst.

    Ich habe mehrere Möglichkeiten aufgezählt, an die Du gedacht haben könntest. Welches davon richtig oder falsch ist, ergibt sich nicht aus deinem kurzen Einführungskommetar ("warum nicht so...").

    [...] Wenn du noch mal nachdenkst, dann kommst du auch, darauf, dass [...]

    [...] Und jetzt mal ehrlich: Hast du bei der private-Geschichte überhaupt versucht [...]

    Nee, Dein Tuple-Ansatz überzeugt mich nicht. Und mit der Einstellung, mit der Du an diese "Diskussion" rangehst, kommst Du nicht weit. Was hast Du damit erreicht? Du hast es geschafft, dass ich kein Bock mehr habe, mit Dir noch ein Wort zu wechseln. Mission accomplished.



  • Dravere schrieb:

    Frage:
    Wie machst du dann sowas:

    template<typename>
    class function;
    
    template<typename R, typename ...ArgTs>
    class function<R(ArgTs...)>
    {
      // ...
    };
    

    Auch wird zum Beispiel Boost.Spirit sicher ähnliches verwenden für die Regeln oder std::bind usw. Es geht hier nicht nur um eine variable Anzahl Parameter bei einer Funktion sondern auch Typen, welche durch eine variable Anzahl anderer Typen definiert werden. Nicht nur std::tuple macht davon Verwendung. Du unterschätzt hier eindeutig das Einsatzgebiet von Variadic Templates.

    Sehr guter Punkt. Das geht wohl nicht so wie ich mir das vorgestellt habe.

    Wobei man sich durchaus überlegen könnte, ob man nicht allgemein eine Typlist als Tuple und damit als Typ ausfassen will. Damit wäre void(int, int, int) dann die selbe Signatur wie void(tuple<int, int, int>). Man müsste auch noch tuple<T> und T gleich setzen damit das funktioniert. Wahrscheinlich wird das aber wohl an irgendwelchen exotischen Konstrukten scheitern die man nicht brechen darf.

    Die Essenz meines Arguments ist, dass wenn man Tuple hat nur noch unäre Funktionen braucht, da man damit alles zusammenbauen kann. C++0x muss an der Stelle also meiner Meinung nach unnötige Redundanz enthalten. Das ist nicht dramatisch, aber ich wäre nicht erstaunt, wenn man das vereinheitlichen könnte.

    Dravere schrieb:

    Sicherlich nur ein sehr kleiner Schutz, aber mich stört dieser static_cast wirklich nicht.

    Mich stört es jedes Mal wenn ich C-Code verbauen soll der malloc verwendet und jedes einzelne Mal frage ich mich was ich dadurch gewonnen habe. Durch manuelles rumfrickeln am Code damit der Compiler ihn frisst baue ich nämlich höchstens Fehler ein.

    Die NULL-Problematik ist auch ein Kind von dieser Fehlentscheidung, da sich nullptr ohne Probleme mit den C-void* als Makro definieren ließ. Naja, hier kann man das Rad der Zeit wohl leider nicht mehr so einfach zurückdrehen auch wenn es mit Abstand die eleganteste Art wäre.

    Dravere schrieb:

    PS: Missverständnisse und andere Meinungen gibt es immer in Diskussionen. Deswegen ist der Diskussionpartner aber nicht gleich dumm und man muss ihn sicherlich nicht von oben herab behandeln.

    Wenn dein Gegenüber aber die Ich-weiß-nicht-warum-aber-das-ist-nun-mal-so-also-hast-du-per-definition-unrecht-Keule auspackt, dann ist es in Internetforen die einzige mir bekannte Möglichkeit ein gewisses Fachniveau zu halten. Es gibt leider keinen zwingenden Grund einen Konsens zu finden und sich des wegen überhaupt mit den Argument des anderen zu beschäftigen.

    Old McDonald schrieb:

    Operatorüberladung kann zu lustigen Effekten führen.
    Oder ist dir mal aufgefallen, dass folgendes gültig ist (kleines C-Überbleibsel)?

    struct X
    {
        struct A { char a[400]; };
        struct B {};
        static B A;
    };
    

    Ich weiß, dass das unwahrscheinlich ist. Aber leider potenziell möglich.

    Interessantes Beispiel. Bist du sicher, dass das auch in C++ gültig ist?

    Ich kann mit Sicherheit sagen, dass zumindest einige MinGW-GCC Versionen damit nicht klar kommen. In der GDI+ gibt (gab) es irgendeinen Fall wo eine Instanz gleich hieß wie ihr Typ und das hat der MinGW zurückgewiesen. Der VC wird es wohl sicherlich schlucken. Die Details kenne ich nicht mehr, es liegt schon mehrere Jahre zurück.

    Wahrscheinlich wäre es sinnvoll mal solche Konstrukte welche niemand verwendet zu depredecaten und eine Warnung vorzuschreiben. Sie sorgen nur für Ärger und nützen niemandem.

    EDIT: Ich hab grad ein bischen mit dem Comeau-Compiler rumgespliet. Das ist scheinbar gültig und sieht so aus, als ob das implizite typedef struct A A; einfach fehlen würde. Dadurch ist X::A eindeutig die Instanz. typename T::A mit T=X ist nicht gültig und damit stellt es im konkreten Fall kein Problem dar.

    In nicht Template-Code kannst du mit struct X::A dennoch eine Instanz anlegen. Ich habe es nicht geschafft das innerhalb eines Templates zu machen. struct typename und typename struct sind ungültig.

    Old McDonald schrieb:

    sizeof...(args) wird es in C++0x geben.

    was neues gelernt, danke.

    Old McDonald schrieb:

    Und wenn du Tupel verwenden willst, kannst du auch einfach

    auto t = make_tuple(args...)
    

    (vielleicht teilweise leicht angepasst) verwenden.

    Das meine ich mit leicht überführbar. Der andere Weg geht auch, auch wenn er syntaktisch schwerfälliger ist.

    audacia schrieb:

    Mit Templates und einem Compiler, der "two-phase name lookup" nicht implementiert, kannst du allerdings eine eine derartige Situation konstruieren. Eine mögliche Gegenmaßnahme wäre, implizite Überladungen auf diesem Wege zu verbieten. Aber da wird dieses ach so einfache Feature natürlich schon wieder kompliziert.

    Stimmt, aber da bei Templates eh alles im Header stehen muss, bringt es aber eh nix außer Konsistenz.

    Bei nicht template Code erlaubt das Konstrukt neue interne Funktionen einführen, ohne den Header zu verändern und damit einen Rebuild von allem Code auszulösen welcher diesen Header einbindet.



  • Ben04 schrieb:

    ...
    Dann wäre es noch sinnvoll zu erlauben private Methoden außerhalb der Klasse zu deklarieren. Diese sind ein Implementierungsdetails welches nicht benötigt werden um die Größe eines Objekts zu berechnen. Es gibt also keinen Grund warum man sie in den Header packen müssen soll....

    Die Idee finde ich auch charmant.

    Allerdings sehe ich da auch Einiges an "Verwirrungspotential":
    1.) Wenn nicht wenigstens die Namen "reserviert" wären, könnten hässliche Namenskollisionen mit abgeleiteten Klassen auftreten (die nicht der Compiler erkennen kann).
    2.) Dass private "Attribute" (die durchaus Einfluß auf die Objektgröße haben und genauso "Implementierungsdetail" sind) nicht ausgelagert werden können, empfinde ich als sehr unschön.
    Spätestens bei Funktionsobjekten wirds da inkonsistent...

    Ob man das jetzt als "Grund, warum man sie in den Header packen muss" einstuft, weiß ich nicht - mir persönlich fällt nur keine andere Lösungsmöglichkeit ein.

    BTW:

    Beispiel schrieb:

    ...

    // MyClass.hpp
    
    class MyClass
    {
    public:
      void foo(char c) { fooPrivate(c); }
      // Ist sowas dann noch möglich? fooPrivate wäre nicht bekannt!
    };
    
    // MyClass.cpp
    
    private void MyClass::fooPrivate(int val)
    {
      // ...
    }
    
    // EvilUser.cpp
    
    private void MyClass::fooPrivate(char c)
    {
      cout << "HrHrHr" << endl;
    }
    

    😉

    Das geht heute auch schon (natürlich mit Funktionsdekl. in der Klasse):

    // MyClass.hpp
    class MyClass
    {
    public:
      void foo(char c) { fooPrivate(c); }
    private: 
      void MyClass::fooPrivate(int val)
    };
    
    // MyClass.cpp
    void MyClass::fooPrivate(int val){ }
    
    // EvilUser.cpp
    namespace {
       void evil(char c) {      cout << "HrHrHr" << endl;   }
    }
    void MyClass::fooPrivate(int c) // Ja - hier noch int
    {
      evil(c);
    }
    

    Gruß,

    Simon2.

    P.S.: @Ben04: Ich mag ja leidenschaftliche Typen, aber überlege Dir doch mal in einer ruhigen Minute, wann der hohe Adrenalingehalt Deiner Beiträge die Ohren Anderer für Dein Anliegen wirklich weiter geöffnet hat ... 😉



  • Simon2 schrieb:

    1.) Wenn nicht wenigstens die Namen "reserviert" wären, könnten hässliche Namenskollisionen mit abgeleiteten Klassen auftreten (die nicht der Compiler erkennen kann).

    Das Problem tritt aber auch jetzt schon auf, weil zuerst gekuckt wird worauf sich ein Identifier bezieht und dann erst wird der Zugriff geprüft. Du kannst also mit privaten Members globale ungewollt verdecken. Den privaten Teil einer Basisklasse zu verändern kann dazu führen, dass eine Abgeleitete bricht. Wenn du Namen aus der Klasse raus bewegst, dann verkleinerst du das aktuelle Problem sogar geringfügig.



  • Objective-C hat ein Feature namens category um uA das private Problem zu lösen. So wirklich toll ist das aber auch nicht. Ideal wäre es, wenn eine Klasse keine geschlossene Einheit ist, sondern man jederzeit neue Funktionen hinzufügen könnte...

    Für das malloc problem kannst du einfach ein #define machen und malloc ein objekt returnen lassen dass sich implizit in alle zeiger typen konvertieren lässt. im idealfall lässt man natürlich den c code in einer .c datei, aber das geht ja leider nicht immer...



  • Shade Of Mine schrieb:

    Ideal wäre es, wenn eine Klasse keine geschlossene Einheit ist, sondern man jederzeit neue Funktionen hinzufügen könnte...

    So wie mit Extension Methods oder Class Helpers?



  • Shade Of Mine schrieb:

    Objective-C hat ein Feature namens category um uA das private Problem zu lösen. So wirklich toll ist das aber auch nicht.

    Können diese Erweiterungsfunktionen dann auch auf die Interna der Klasse zugreifen oder diese gar durch zum Beispiele neue Datenmember erweitern? Ich gehe mal davon aus, dass dies nicht möglich ist. Ich kenne mich mit Obj-C leider gar nicht aus und so aussagekräftig ist der Wikipedia Artikel leider nicht. 😞

    Shade Of Mine schrieb:

    Ideal wäre es, wenn eine Klasse keine geschlossene Einheit ist, sondern man jederzeit neue Funktionen hinzufügen könnte...

    Vielleicht. Mein Bauchgefühl tendiert da aber in eine andere Richtung. In C++ hast du die Möglichkeit freie Funktionen zu erstellen. Ich würde es für sinnvoller halten das Klasseninterface minimal zu halten und dann komplexere Funktionalität mit freien Funktionen aufzubauen.



  • Ben04 schrieb:

    Können diese Erweiterungsfunktionen dann auch auf die Interna der Klasse zugreifen oder diese gar durch zum Beispiele neue Datenmember erweitern?

    Datenmember muss ich zugeben, weiss ich nicht. Ich denke ja, würde aber meine Hand dafür nicht ins feuer legen. Eine category kann private daten haben, aber ob eine andere category dann darauf zugreifen kann - das weiss ich jetzt nicht.

    zugriff auf private member hat eine category aber.

    Vielleicht. Mein Bauchgefühl tendiert da aber in eine andere Richtung. In C++ hast du die Möglichkeit freie Funktionen zu erstellen. Ich würde es für sinnvoller halten das Klasseninterface minimal zu halten und dann komplexere Funktionalität mit freien Funktionen aufzubauen.

    mir persönlich gefällt dieses "willkürliche" trennen zwischen member und freistehende funktion irgendwie nicht. natürlich ist die trennung nicht wirklich willkürlich, aber als anwender der klasse sind mir oft die design entscheidungen dahinter egal.

    zB list::sort versus std::sort. natürlich macht es sinn dass eine list ein eigenes sort anbietet, aus einer technischen sicht, aber wenn ich das ding nur sortieren will, ist mir das eigentlich egal. wobei das jetzt von der signatur her ein dummes beispiel ist.

    categories erlauben mir zB ähnlich dem prototype feature aus sprachen wie javascript, neue methoden zu einer bestehenden klasse hinzuzufügen und auch bestehende funktionen zu überschreiben. vl wäre es auch toll wenn o.f() und f(o) identisch wären... ich weiss nicht was mir da am besten gefallen würde. ich weiss nur, dass mir diese trennung member <-> freistehend nicht wirklich gefällt 😕

    @audacia:
    ja, so etwas wie extension methods. class helpers kenne ich nicht.



  • Shade Of Mine schrieb:

    zB list::sort versus std::sort. natürlich macht es sinn dass eine list ein eigenes sort anbietet, aus einer technischen sicht, aber wenn ich das ding nur sortieren will, ist mir das eigentlich egal. wobei das jetzt von der signatur her ein dummes beispiel ist.

    Im Normalfall löst man so etwas über Spezialisierung/Überladung. Mir sind einige Fälle bekannt in denen das auch in der Praxis gut klappt.

    Der Grund warum es in diesem konkreten Fall glaube ich nicht anders geht, ist dass du zum Sortieren das std::list Objekt selbst brauchst und dieses nicht aus den Iteratoren alleine bestimmen kannst.

    Bei Klassenerweiterungen welche uneingeschränkt Zugriff auf die privaten Daten haben geht dir die Kapselung leider kaputt und ab einem gewissen Komplexitätsgrad deines Programms geht es einfach nicht mehr ohne. Das Problem ist halt, dass wenn du deine Klasseninterna änderst du Gefahr läufst, dass alle Erweiterungen zerbrechen.

    Ob jetzt wirklich jede einzelne Klasse derart abgeschottet werden muss wie es C++ macht ist aber auch nicht klar. Mir gefällt das package Zugriffsrecht aus Java ganz gut. Das macht die Kapsel ein wenig größer, so dass eine handvoll Klassen rein passen aber nicht zu groß, als dass man Wartungsprobleme kriegen würde. Dadurch braucht man auch 90% der friends nicht mehr.




  • Administrator

    drakon schrieb:

    @Dravere: Warst du da?

    Habe angefragt und mitgeteilt bekommen, dass meine Anwesenheit unerwünscht wäre, daher bin ich nicht gegangen. Habe mich auch die ganze Woche aus Protest mit C# und Lua beschäftigt 😃 🤡

    Grüssli



  • Dravere schrieb:

    drakon schrieb:

    @Dravere: Warst du da?

    Habe angefragt und mitgeteilt bekommen, dass meine Anwesenheit unerwünscht wäre

    😮 😕

    Die Vorträge sind öffentlich zugänglich, die Teilnahme ist kostenlos.

    Aus dem Link oben.



  • Ben04 schrieb:

    Und jetzt mal ehrlich: Hast du bei der private-Geschichte überhaupt versucht ein Beispiel zu basteln, das die Kapselung bricht? Wenn nicht dann ist mein Gebashe gerechtfertigt.

    Im Zusammenspiel mit virtuellen Funktionen kann man damit hübsch Unfug treiben.

    Und, wenn man die Implementierung kennt, also die Signaturen von privaten Funktionen kennt, kann man auch mit Overload-Resolution schlimme Dinge machen.



  • drakon schrieb:

    Mist.

    Ich habs komplett verpasst. 😞
    http://www.hsr.ch/Agenda.6479.0.html?&uid=45&tx_nmagenda_pi_agenda_detail[view]=detail&cHash=640412452a

    @Dravere: Warst du da?

    *g* Ich war da, am SWEN Talk... 🙂


Anmelden zum Antworten