std::mem_movable



  • pumuckl schrieb:

    Unintuitiv, aber nicht inkonsistent. Inkonsistenz würde bedeuten, dass der Standard sich selbst widerspricht.

    pumuckl schrieb:

    Ich gehe sogar soweit zu behaupten dass so ein Feature nicht konsistent in die Sprache eingeführt werden könnte.

    Ich bin davon ausgegangen, dass du "inkonsist" im Sinne von "sich intuitiv nachvollziehbar verhalten" meintest. Dass ein Standard so ein feature wie std::mem_movable beinhalten und trotzdem formal widerspruchsfrei sein kann halte ich für sehr wahrscheinlich, da ja nur bisher undefined behaviour zu defined behaviour würde (ob es den Aufwand wert ist und Sinn ergibt ist natürlich fraglich).

    pumuckl schrieb:

    don't pay what you don't use wird verletzt:
    - rtti
    - exceptions (war es nicht so dass die compiler dann nicht so gut optimieren
    können? wobei exceptions ein Programm auch schneller machen können, hier gibt es wohl keinen Königsweg)

    Wo wird da was verletzt? Für Klassen ohne virtuelle Funktionen gibts auch kein RTTI. Wenn man keine Exceptions benutzt, verlangt der Standard auch keinen Overhead dafür. (Was die Compiler draus machen ist was anderes)

    Der Standard ist nicht als Beschreibung einer nur hypothetischen Sprache entworfen worden. Ein Ziel von c++ ist es, keinen unnötigen overhead im Vergleich zu c zu verursachen. Das ist bei den heutigen compilern in den von mir genannten Punkten nicht gegeben. Vorgabe und Umsetzung widersprechen sich hier (bei exceptions finde ich das aber gut so wie es ist).

    pumuckl schrieb:

    [*] die Standardbibliothek ist mit der Sprache verwurstelt: typeid, dynamic_cast und sizeof

    Mal abgesehen davon dass weder dynamic_cast noch sizeof irgendwas mit der Standardbibliothek zu tun haben: Was macht den Standard in sich inkonsistent, wenn zwei seiner Features (typeid-Operator und type_info Klasse) voneinander abhängig sind? Beide sind Teil des Standards, genauso wie Klassen und Memberfunktionen - letztere würden ohne erstere auch keinen Sinn machen.

    Welchen Typ hat die von dynamic_cast geworfene exception und wo ist der definiert? Was ist der Rückgabetyp von sizeof?
    Die standard library ist doch deshalb eine Bibliothek, weil man sie austauschen und etwas anderes stattdessen verwenden kann. Für einen sinnvollen Einsatz von typeid (und eingeschränkt auch dynamic_cast und sizeof) kann man aber nicht auf sie verzichten.

    pumuckl schrieb:

    Da bist du einem Irrtum aufgesessen. Derived sieht nur ::A, nicht Base::A.

    error: 'class Base::A' is private
    

    Im name lookup verdeckt Base::A ::A. Private Member, also die Implementierung von Base, beeinflussen, wie sich Derived verhält. Man kann eine Klasse nicht als black box schreiben, da alle privaten member sich in dieser Form bemerkbar machen. Das widerspricht der Vorgabe von c++ als OOP-Sprache.

    pumuckl schrieb:

    Und? Wo ist die Inkonsistenz? Dass du mit den Regeln auf dem Kriegsfuß stehst und Probleme damit hast, bedeutet doch nicht, dass der Standard inkonsistent ist.

    Der gleiche token steht für 2 völlig unterschiedliche Dinge. Das ist nicht gerade intuitiv.

    cooky451 schrieb:

    1. @RTTI -> Hm.. man zahlt dafür wenn man es nicht nutzt? Das ist mir neu.

    Was ist, wenn du ein Objekt an eine dll übergibst, die dann typeid aufruft, um den Namen des Typs zu bekommen? Dein Programm muss, selbst wenn dein compiler so schlau ist und das ganze Programm analysiert, irgendwo speichern, wie der Typ hieß. Das muss für jeden Typ passieren, der in nicht einsehbarem code verwendet wird.

    2. @exceptions -> Ohne try/catch hast du keine Nachteile. Und try/catch zu nutzen ohne etwas zu werfen ist irgendwie.. suboptimal.

    Dazu kann ich nichts Näheres sagen, habe aber gelesen, dass compiler bestimmte Optimierungen bei Aktivierung von exceptions nicht machen können.

    Es geht mir ja gar nicht darum, c++ schlecht zu reden, die Sprache ist halt schon 30-40 Jahre alt, dafür hat sie sich ganz gut gehalten. Ich kenne keine Sprache, die so eine Bandbreite von Bitfrickelei bis starke Abstraktion bietet.


  • Mod

    bmario_ schrieb:

    Nexus schrieb:

    Wie sieht es aber mit dem typename aus, warum genau ist das bei Vererbungs- und Initialisierungsliste verboten? Ich würde hier übrigens auch von Inkonsistenz sprechen, dazu muss kein Widerspruch vorhanden sein.

    Ich kann leider nicht mit einem Auszug aus dem Standard dienen, aber Scott Meyers wird das sicher nicht aus Spass in Effective C++ so beschreiben. Er erwähnt aber auch, dass das verschiedene Compiler unterschiedlich sehen mit dem Typename.

    C++11 schrieb:

    14.6 Name resolution [temp.res]
    :::
    5 A qualified name used as the name in a mem-initializer-id, a base-specifier, or an elaborated-type-specifier is implicitly assumed to name a type, without the use of the typename keyword. In a nested-name-specifier that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or simple-template-id is implicitly assumed to name a type, without the use of the typename keyword. [ Note: The typename keyword is not permitted by the syntax of these constructs. —end note ]

    C++03 schrieb:

    14.6 Name resolution [temp.res]
    :::
    5 The keyword typename shall only be used in template declarations and definitions, including in the return type of a function template or member function template, in the return type for the definition of a member function of a class template or of a class nested within a class template, and in the type-specifier for the definition of a static member of a class template or of a class nested within a class template. The keyword typename shall be applied only to qualified names, but those names need not be dependent. The keyword typename shall be used only in contexts in which dependent names can be used. This includes template declarations and definitions but excludes explicit specialization declarations and explicit instantiation declarations. The keyword typename is not permitted in a base-specifier or in a mem-initializer; in these contexts a qualified-id that depends on a template-parameter (14.6.2) is implicitly assumed to be a type name.


  • Mod

    GorbGorb schrieb:

    pumuckl schrieb:

    Da bist du einem Irrtum aufgesessen. Derived sieht nur ::A, nicht Base::A.

    error: 'class Base::A' is private
    

    Im name lookup verdeckt Base::A ::A. Private Member, also die Implementierung von Base, beeinflussen, wie sich Derived verhält. Man kann eine Klasse nicht als black box schreiben, da alle privaten member sich in dieser Form bemerkbar machen. Das widerspricht der Vorgabe von c++ als OOP-Sprache.

    Darüber kann man streiten, die Frage hier war aber die nach Inkonsistenz. Und die ist hier nicht erkennbar - im Gegenteil: einer der grundsätzlichen Vorgaben ist die, dass die Bedeutung eines Konstrukts nur von den an der jeweiligen Stelle bereits erfolgten Deklarationen abhängen sollte, nicht aber davon, wo genau das Konstrukt steht.

    class A
    {
    };
    class Base
    {
        class A
        {
        };
        friend class Derived;
    };
    class Derived
        : public Base
    {
        A a;
    };
    

    Jetzt soll dann plötzlich Base::A gemeint sein, dagegen ::A falls Derived kein friend ist? Das halte ich für überraschend.



  • camper schrieb:

    einer der grundsätzlichen Vorgaben ist die, dass die Bedeutung eines Konstrukts nur von den an der jeweiligen Stelle bereits erfolgten Deklarationen abhängen sollte, nicht aber davon, wo genau das Konstrukt steht.

    Das musst du mir noch ein bisschen ausführen:
    Es sollte doch einen Unterschied machen, ob ich A a; in einer Klasse oder auf namespace scope schreibe?

    class A
    {
    };
    class Base
    {
        class A
        {
        };
        friend class Derived;
    };
    class Derived
        : public Base
    {
        A a;
    };
    

    Jetzt soll dann plötzlich Base::A gemeint sein, dagegen ::A falls Derived kein friend ist? Das halte ich für überraschend.

    Ich würde einfach sagen, dass es ambiguous ist und damit ein Fehler. Natürlich macht das dann Probleme, wenn man sich bei

    class A
    {
    };
    class B
    {
    public:
        class A
        {
        };
        A a;
    };
    

    darauf verlässt, dass a den Typ B::A hat und nicht ::A. Aber auch das hätte man ja als Fehler werten können.


  • Mod

    GorbGorb schrieb:

    Ich würde einfach sagen, dass es ambiguous ist und damit ein Fehler. Natürlich macht das dann Probleme, wenn man sich bei

    class A
    {
    };
    class B
    {
    public:
        class A
        {
        };
        A a;
    };
    

    darauf verlässt, dass a den Typ B::A hat und nicht ::A. Aber auch das hätte man ja als Fehler werten können.

    Ich kann schon den fluchenden Maintainer sehen, der zusätzlich Arbeit bekommt, weil Bibliothek A einen neuen Bezeichner eingeführt hat der zufällig mit einem Klassenmember der Bibliothek B kollidiert und außerdem zufällig der Header für A zuerest eingebunden wird.



  • Genauso kann ein maintainer fluchen weil eine Klasse, von der er ableitet, einen neuen privaten member bekommt, der halt zufällig genauso heißt wie eine Klasse aus dem namespace der Klasse, die ableitet.


  • Mod

    GorbGorb schrieb:

    Genauso kann ein maintainer fluchen weil eine Klasse, von der er ableitet, einen neuen privaten member bekommt, der halt zufällig genauso heißt wie eine Klasse aus dem namespace der Klasse, die ableitet.

    In diesem Szenario besthet allerdings ein fundamentaler Unterschied: deine abgeleitete Klasse hängt bereits sowohl von ihrer Basisklasse als auch diesem namespace-Member ab. Es kann nicht überraschen, dass Änderungen an Teilen, zu denen Abhängigkeiten bestehen, potentiell zu Kollisionen führen.

    In meinem Szenario hingegen besteht überhaupt keine Abhängigkeit von diesem Bezeichner aus A; die Kollision ist dabei völlig zufällig und kann nicht von vornherein verhindert werden.



  • GorbGorb schrieb:

    Ich bin davon ausgegangen, dass du "inkonsist" im Sinne von "sich intuitiv nachvollziehbar verhalten" meintest. Dass ein Standard so ein feature wie std::mem_movable beinhalten und trotzdem formal widerspruchsfrei sein kann halte ich für sehr wahrscheinlich, da ja nur bisher undefined behaviour zu defined behaviour würde (ob es den Aufwand wert ist und Sinn ergibt ist natürlich fraglich).

    Genau das halte ich für unwahrscheinlich. Es würde nicht reichen, mem_movable einzuführen und ein paar kleiner Regelungen anzupassen. Stattdessen einige Dinge komplett rausgeworfen oder umgeschrieben werden, die aktuell wichtige "Verhaltensfeatures" der Sprache sind.



  • camper schrieb:

    GorbGorb schrieb:

    Genauso kann ein maintainer fluchen weil eine Klasse, von der er ableitet, einen neuen privaten member bekommt, der halt zufällig genauso heißt wie eine Klasse aus dem namespace der Klasse, die ableitet.

    In diesem Szenario besthet allerdings ein fundamentaler Unterschied: deine abgeleitete Klasse hängt bereits sowohl von ihrer Basisklasse als auch diesem namespace-Member ab. Es kann nicht überraschen, dass Änderungen an Teilen, zu denen Abhängigkeiten bestehen, potentiell zu Kollisionen führen.

    In meinem Szenario hingegen besteht überhaupt keine Abhängigkeit von diesem Bezeichner aus A; die Kollision ist dabei völlig zufällig und kann nicht von vornherein verhindert werden.

    Ich kann genauso argumentieren, dass eine Abhängigkeit von A s namespace besteht (sonst würde er ja per using namespace eingebunden), mit einer Namenskollision bei einer Änderung an diesem namespace muss also gerechnet werden (das ist ja der Grund, warum man using namespace sehr vorsichtig benutzen sollte).

    @pumuckl: An welche features hast du da gedacht?



  • GorbGorb schrieb:

    @pumuckl: An welche features hast du da gedacht?

    Vor allem an die Dinge, die der Standar als object lifetime und ibjekt identity bezeichnet. Wenn es mem_movable für etwas anderes als PODs gibt, dann werden diese Konzepte über den Haufen geworfen. Für PODs gibts dagegen afaik schon ein Trait in der Art, also wäre ein mem_movalbe dafür hinfällig.


  • Mod

    GorbGorb schrieb:

    Ich kann genauso argumentieren, dass eine Abhängigkeit von A s namespace besteht (sonst würde er ja per using namespace eingebunden), mit einer Namenskollision bei einer Änderung an diesem namespace muss also gerechnet werden (das ist ja der Grund, warum man using namespace sehr vorsichtig benutzen sollte).

    Verstehe ich nicht.

    Wobei ich kein besonderes Interesse an dieser Diskussion habe, weil ich nicht erkennen kann, wo sie hinführen soll. Vererbung beschränkt sich in C++ nicht nur auf das Interface, dass damit auch als private deklarierte Namen sichtbar werden, ist nur konsequent. Etwas anderes würde Sonderregeln erfordern, also gerade zur angeblichen Inkonsistenz beitragen.


Anmelden zum Antworten