Statische Klassenkonstante von Basisklasse in Subklasse verwenden


  • Mod

    KasF schrieb:

    camper schrieb:

    womit willst du sonst initialisieren?

    Mit einem Lvalue, zB einer Funktionsrückgabe.

    Hier werden Begriffe falsch verwendet. Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte. Das Ergebnis eines Funktionsaufrufes ist ein Rvalue des Rückgabetyps (es sei denn, dieser Typ ist eine Referenz - dann ist der entsprechende Aufruf, wie in jedem analogen Kontext, ein Lvalue des Typs, auf den die Referenz verweist).
    Die entscheidende Frage ist hier, ob die Konstante mit einem konstanten Ausdruck initialisiert wurde.
    Ein Primärausdruck, der auf eine konstante Variable verweist, ist genau dann ein konstanter Ausdruck, wenn diese konstante Variable (lexikalisch) zuvor mit einem konstanten Ausdruck initialisiert wurde. Dabei spielt es wiederum keine Rolle, ob diese Initialisierung in der Klasse oder außerhalb stattgefunden hat. dass wir sie in Fällen wie diesem nicht außerhalb machen, hat andere Gründe - ODR.

    struct Foo
    {
        static const int x = 1;
        static const int y;
    };
    
    int foo = Foo::x; // Foo::x ist konstanter Ausdruck -> statische Initialisierung
                      // Foo::x muss trotzdem noch definiert werden, denn dies ist kein Kontext, in dem ein konstanter Ausdruck stehen muss
    const int Foo::x;
    int bar = Foo::y; // Foo::y ist kein konstanter Ausdruck -> dynamische Initialisierung
                      // Foo::y kann hier nicht als case-Label benutzt werden
    
    const int Foo::y = 2; // 2 ist als Literal (natürlich) ein konstanter Ausdruck
    
    int baz = Foo::y; // Foo::y ist konstanter Ausdruck -> statische Initialisierung
                      // Foo::y könnte hier als case-Label benutzt werden
    


  • camper schrieb:

    Hier werden Begriffe falsch verwendet. Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte. Das Ergebnis eines Funktionsaufrufes ist ein Rvalue des Rückgabetyps

    Das habe ich auch bis dato immer so gedacht, bis ich beim Comeau Compiler und in diverser Literatur darauf gestoßen bin, dass es modifiziberbare Lvalues und nicht modifizierbare Lvalues gibt.

    Beispiel:

    const int t  = 4; 
    t = 33;               // Error, t ist ein nicht modifizierbarer Lvalue ?
    f() = 4;              // Error, Rückgabe ist ein Rvalue ?
    4 = 3;                // Error, 4 ist Rvalue ? 
    4 + 3 = 2;           // the same ?
    

    Richtig so ?

    Ich habe das mal ausprobiert und Comeau sagt überal nicht modifizierbarer Lvalue. Das Wort Rvalue ist gar nicht aufgetaucht.

    Klarheit bitte ? 🙂


  • Mod

    KasF schrieb:

    Ich habe das mal ausprobiert und Comeau sagt überal nicht modifizierbarer Lvalue.

    Da steht

    expression must be a modifiable lvalue

    - mit anderen Worten, der fehlerhafte Ausdruck ist kein modifizierbarer Lvalue. Da steht nicht, ob der Ausdruck zwar ein Lvalue sei, aber nicht modifizierbar (trifft in erste Zeile zu), oder schlicht kein Lvalue ist (die anderen 3 Fälle).
    Skalare Rvalues sind niemals modifizierbar (weil sie keine Objekte repräsentieren) - tatsächlich geht ja jede cv-Qualifikation verloren, wenn man den Wert eines Objektes liest (=den Lvalue-Ausdruck in einen Rvalue-Ausdruck konvertiert).
    Bei der Initialisierung eines sklaren Objektes, benötigen wir eine Wert für das Objekt, also ein Rvalue. Steht dort aber ein Lvalue, wird dieses implizit in ein Rvalue konvertiert (also der Wert gelesen).



  • camper schrieb:

    mit anderen Worten, der fehlerhafte Ausdruck ist kein modifizierbarer Lvalue. Da steht nicht, ob der Ausdruck zwar ein Lvalue sei, aber nicht modifizierbar (trifft in erste Zeile zu), oder schlicht kein Lvalue ist (die anderen 3 Fälle).

    Oh :), vielen Dank wiedermal an dich camper für die Aufklärung.



  • MrGentleman schrieb:

    Naja - schlechtes design - das ganze soll ein scanner für einen parser werden; und da muss ich halt abhängig des gelesenen zeichens ein entsprechendes token generieren.

    Das Interface von Base und Derived ergibt irgend wie überhaupt keinen Sinn. Wozu eine Ableitung und dort eine Implementation? Polymorphie kommt nicht zum Einsatz, und wenn sie zum Einsatz käme wäre wohl eine Factory Klasse als Singleton, die Objekte erzeugt, die dann die eigentliche Aufgabe übernehmen sinnvoller.

    MrGentleman schrieb:

    wie meinst du es mit delegates?

    Ein Implementierungsdetail ist damit gemeint. Meist taucht der Begriff in Zusammenhang mit SmallTalk Patterns auf z.B. mit Object Recursion - Chain of Command ist nur ein Spezialfall davon. Man kann in C++ Delegation via Funktionszeiger, Funktoren oder speziellen Klassen (Funktoren sind das ja auch) umsetzen, es kommt halt darauf an, was man braucht.



  • Nochmal nachgehakt:

    camper schrieb:

    Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte.

    const int t  = 4;
    

    t ist somit ein Lvalue, aber ein nicht modifizierbarer ?


  • Mod

    KasF schrieb:

    Nochmal nachgehakt:

    camper schrieb:

    Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte.

    const int t  = 4;
    

    t ist somit ein Lvalue, aber ein nicht modifizierbarer ?

    t ist eine konstante Variable.
    Der Ausdruck

    t
    

    ist ein nichtmodifizierbares Lvalue (Lvalue: Ausdruck verweist auf das skalare Objekt t; nichtmodifizierbar: da const-qualifiziert).



  • Simon2 schrieb:

    aber ganz überflüssig ist es nicht (und sei es nur, um aufgrund vom Input die richtige Klasse zu instantiieren).

    Es trifft aber nur auf die wenigen Fälle zu in denen man nur Integereingaben zu verarbeiten hat. Bei Scannern/Parsern hatte ich bisher immer das Glück, daß es sich um Zeichenketten handelte und nicht um einzelne Buchstaben.



  • MrGentleman schrieb:

    switch (ch) {
      case Scanner::_EOF:
        _token->type = Token::_EOF;
    	break;
    
      case MyConcreteScanner::QUOTE:
        _token->type = Token::QUOTE;
        nextChar();
    	break;
    

    Das meinte ich mit schlechtem Design (in Kombination zu Deinen erstem Posting). Du scheinst C und OOP Programmierung zu vermischen. Falls du wegen der Geschwindigkeit ein C-ähnlichen Ansatz brauchst, dann brauchst Du keine Vererbunghierachie, und wenn es OOP sein soll, was soll dann bitte "_token->type" für eine Aufgabe übernehmen?



  • Leute habe folgendes Programm geschrieben:

    #include <cstdlib>

    #include <iostream>

    using namespace std;

    int main(int argc, char *argv[])
    {

    int Preis, Rabatt, Endpreis ;

    cout << " Berechnung des Endpreises " ;

    cout << " Geben Sie den Preis ein " ;
    cin >> Preis ;

    cout << " 8% Großkunde, 5% Stammkunde oder 0% Sonstige ? " ;

    cin >> 8% >> 5% >> 0% ;

    int Auswahl ;

    switch(Auswahl)

    {
    case 1: Auswahl= 8% ;
    break ;

    case 2: Auswahl= 5% ;
    break ;

    case 3: Auswahl = 0% ;
    break ;

    default: Auswahl = 15% ;

    }

    Endpreis = Preis * 8% ;

    Endpreis = Preis * 5% ;

    Endpreis = Preis * 0% ;

    cout << " Endpreis ist gleich " << Endpreis ;

    system("PAUSE");
    return EXIT_SUCCESS;
    }



  • Ich hoffe, jemand kann mir helfen


Anmelden zum Antworten