Warum hat C++ so eine aufwendige Syntax?



  • Dennis123 schrieb:

    Also meiner bescheidenen Meinung nach ist das sauber und effizient formatiert.
    Alles andere wäre nur eine unnötige verschwendung von Platz etc.

    Wie kann man etwas verschwenden, von dem man unendlich viel hat? :p



  • Undertaker schrieb:

    Michael E. schrieb:

    En Konstruktor, der zur Compile Time ausgeführt wird? 😮

    das geht nicht?
    habe ich tatsächlich die mächtigkeit der C++ templates so masslos überschätzt? 😮

    Nein, du hast sie gar nicht erst verstanden 🤡



  • blablablabla schrieb:

    Undertaker schrieb:

    Michael E. schrieb:

    En Konstruktor, der zur Compile Time ausgeführt wird? 😮

    das geht nicht?
    habe ich tatsächlich die mächtigkeit der C++ templates so masslos überschätzt? 😮

    Nein, du hast sie gar nicht erst verstanden 🤡

    was fehlt mir zum verständnis? warum gibt es keine compile-time constructors in C++?
    es gibt klassen-templates. haben die keine konstruktoren?



  • Undertaker schrieb:

    blablablabla schrieb:

    Undertaker schrieb:

    Michael E. schrieb:

    En Konstruktor, der zur Compile Time ausgeführt wird? 😮

    das geht nicht?
    habe ich tatsächlich die mächtigkeit der C++ templates so masslos überschätzt? 😮

    Nein, du hast sie gar nicht erst verstanden 🤡

    was fehlt mir zum verständnis? warum gibt es keine compile-time constructors in C++?
    es gibt klassen-templates. haben die keine konstruktoren?

    Doch, haben sie, die werden aber wie alle Konstruktoren zur Laufzeit ausgeführt.



  • Mr. N schrieb:

    Undertaker schrieb:

    blablablabla schrieb:

    Undertaker schrieb:

    Michael E. schrieb:

    En Konstruktor, der zur Compile Time ausgeführt wird? 😮

    das geht nicht?
    habe ich tatsächlich die mächtigkeit der C++ templates so masslos überschätzt? 😮

    Nein, du hast sie gar nicht erst verstanden 🤡

    was fehlt mir zum verständnis? warum gibt es keine compile-time constructors in C++?
    es gibt klassen-templates. haben die keine konstruktoren?

    Doch, haben sie, die werden aber wie alle Konstruktoren zur Laufzeit ausgeführt.

    also mal angenommen ich habe sowas

    template <typename T>
    struct A
    {
        T i;
    
        A(T x)
        {
            i = x * x;
        }
    
        T get (void)
        {
            return i;
        }
    };
    

    und rufe es so auf:

    int x = A<int>(3).get();
    

    dann ist es doch das gleiche, als wenn ich schreiben würde

    int x = 9;
    

    weil der compiler ja den template-code selber ausrechnet. es steht ja alles schon zur compile-time fest. es sollte doch kein funktionsaufruf o.ä. erzeugt werden.
    oder nicht?



  • Das ist ein typsicher Fall für den Optimierer, aber hat nichts mit Templates zu tun. In C wird hier auch nicht - obwohl alles bekannt ist - direkt ein Ergebnis hingeschrieben (klar, macht dann der Optimierer...):

    int add (int i1, int i2) { return i1 + i2; }
    
    ...
    
    int j = add (5,7); // ist genauso j = 12
    

    Aber keine Sorge, darum kümmert sich dann der Compiler. Mit der Meta-Programmierung ist es in C++ aber im Gegensatz zu C tatsächlich möglich hier auch zur Compile-Time das Ergebnis zu berechnen!

    MfG SideWinder



  • Undertaker schrieb:

    es gibt klassen-templates. haben die keine konstruktoren?

    Nein. Was sollte der konstruieren? Ein Klassentemplate erzeugt Klassen, keine Objekte. Andererseits ist der Gedanke ja nicht ganz unsinnig: wenn es nur darum geht, Ausdrücke, deren Wert bereits während des Compilierens feststeht, zu verwenden, so ist die Benutzung von Templates (um den Compiler zu zwingen, die Berechnung gleich durchzuführen) eigentlich mit Kanonen auf Spatzen geschossen. Einerseits ist die Templatesyntax ziemlich barock und mit einigen Stolperfallen versehen, anderseits ist es im Wesentlich nur für integrale Typen möglich. Nicht ganz zufällig wird es C++0x nutzerdefinierte Literale und Erweiterung hinsichtlich konstanter Ausdrücke (Schlüsselwort constexpr) geben.



  • Undertaker schrieb:

    also mal angenommen ich habe sowas

    template <typename T>
    struct A
    {
        T i;
        
        A(T x)
        {
            i = x * x;
        }
        
        T get (void)
        {
            return i;
        }
    };
    

    und rufe es so auf:

    int x = A<int>(3).get();
    

    dann ist es doch das gleiche, als wenn ich schreiben würde

    int x = 9;
    

    weil der compiler ja den template-code selber ausrechnet. es steht ja alles schon zur compile-time fest. es sollte doch kein funktionsaufruf o.ä. erzeugt werden.
    oder nicht?

    Schau dir mal diesen Code an:

    struct A_int {
        int i;
    
        A(int x)
        {
            i = x * x;
        }
    
        int get()
        {
            return i;
        }
    };
    
    int x = A_int(3).get();
    

    Der Code ist mit deinem vergleichbar. Der Compiler kann in beiden Fällen

    int x = 9;
    

    draus machen. Metaprogrammierung wäre was anderes:

    template<int x>
    struct A {
      static const int get = x * x;
    };
    int x = A<3>::get;
    

    Hier ist garantiert, dass der Compiler die Berechnung zur Compile-Zeit ausführt.



  • Shade Of Mine schrieb:

    ...
    von value typen erbt man nunmal nicht. ...
    ...hätte sie final/sealed zu sein. da man eben von ihr nicht erben soll. ergo verstehe ich das ganze nicht.

    "Du sollst nicht von int erben !" ? Elftes Gebot ?

    Also diese moralischen Grenzen kann ich in dem Zusammenhang nicht recht akzeptieren. Es wäre schön, etwas zu haben, was den Intellekt anspricht.
    😃

    Gruß,

    Simon2.

    P.S.: In meinem Beispiel hieße das dann "Man soll eben nicht dieselbe Weltformel für Zwerpinscher wie für alle anderen haben !"



  • Mr. N schrieb:

    template<int x>
    struct A {
      static const int get = x * x;
    };
    int x = A<3>::get;
    

    Hier ist garantiert, dass der Compiler die Berechnung zur Compile-Zeit ausführt.

    und was ist damit:

    template <typename T>
    T square (T x)
    {
        return x * x;
    }
    ...
    int x = square<int>(3);
    ...
    

    ?
    ist dabei garantiert, dass die berechnung beim compilieren gemacht wird, oder auch nicht?



  • EDIT://
    stand ja schon oben ...



  • Undertaker schrieb:

    Mr. N schrieb:

    template<int x>
    struct A {
      static const int get = x * x;
    };
    int x = A<3>::get;
    

    Hier ist garantiert, dass der Compiler die Berechnung zur Compile-Zeit ausführt.

    und was ist damit:

    template <typename T>
    T square (T x)
    {
        return x * x;
    }
    ...
    int x = square<int>(3);
    ...
    

    ?
    ist dabei garantiert, dass die berechnung beim compilieren gemacht wird, oder auch nicht?

    Du kannst ja sämtliche deiner Vorschläge selbst testen. Kombiniert man beide Beispiele müsste folgendes funktionieren, wenn es denn zur Compile-Time berechnet wird.

    int x = A<square<int>(3)>::get;
    

    Das macht eben den Unterschied aus, wobei ich auch sagen muss, dass dieses Beispiel auch gekünstelt ist und ich mir hier eben wirklich keinen Fall vorstellen kann, indem ich zur "echten" Compile-Time das Quadrat von 3 brauche.



  • dsfsdf schrieb:

    int x = A<square<int>(3)>::get;
    

    error C2975: 'x' : invalid template argument for 'A', expected compile-time constant expression

    offenbar wird das function template nicht zur compile-time berechnet.
    ...und ich dachte, templates wären eine tolle sache 🙄
    jetzt bin ich aber echt enttäuscht 😞



  • Wozu braucht man das ueberhaupt? Ich mein, es ist zwar schoen wenn der Compiler mir etwas ausrechnen kann, aber das sind doch alles Konstanten? Also mann kann doch nicht waerend das Programm laeuft, etwas vom Compiler ausrechnen lassen.

    Beispiel, dieses Meta-Programm um zu testen ob eine Zahl eine perfekte ist. Man kann es doch nicht so machen, dass der Benutzer eine Zahl eingibt, und dann wird getestet, ob die Zahl perfekt ist. Sondern man gibt eine Zahl vor, dann laest man das Programm kompelieren und dann hat man das Ergebnis. Dann kann ich aber gleich ein "normales" Programm schreiben.

    Gibts da irgendeinen reallen Mehrwert?



  • Undertaker schrieb:

    ...und ich dachte, templates wären eine tolle sache 🙄
    jetzt bin ich aber echt enttäuscht 😞

    Ach, weil du keine Ahnung hast, bist du enttäuscht? Nicht wissen, was TMP ist, aber dann mal drüber ablästern...



  • Michael E. schrieb:

    Undertaker schrieb:

    ...und ich dachte, templates wären eine tolle sache 🙄
    jetzt bin ich aber echt enttäuscht 😞

    Ach, weil du keine Ahnung hast, bist du enttäuscht? Nicht wissen, was TMP ist, aber dann mal drüber ablästern...

    klar bin ich enttäuscht, wenn's noch nicht mal bei so einfachen sachen wie x*x funktioniert. stattdessen muss man mit komischen structs mit 'static const' membern rumfrickeln und kann noch nicht mal normale kontrollstrukturen wie if/else einsetzen?
    das ist doch nur noch zum heulen 😞



  • Dann geh doch heulen und verschon uns.



  • Undertaker schrieb:

    Michael E. schrieb:

    Undertaker schrieb:

    ...und ich dachte, templates wären eine tolle sache 🙄
    jetzt bin ich aber echt enttäuscht 😞

    Ach, weil du keine Ahnung hast, bist du enttäuscht? Nicht wissen, was TMP ist, aber dann mal drüber ablästern...

    klar bin ich enttäuscht, wenn's noch nicht mal bei so einfachen sachen wie x*x funktioniert. stattdessen muss man mit komischen structs mit 'static const' membern rumfrickeln und kann noch nicht mal normale kontrollstrukturen wie if/else einsetzen?

    Wieso ist ein Konstrukt schlechter, weil dus komisch findest? Nur mal ein klitzekleines Beispiel für TMP:

    template<
          typename Graph,
          typename First = typename boost::mpl::begin<Graph>::type,
          typename Last = typename boost::mpl::end<Graph>::type,
          typename Result = boost::mpl::map0<>
        >
        struct clean_graph {
          typedef typename boost::mpl::deref<First>::type::first key;
          typedef typename boost::mpl::if_<
              typename boost::mpl::has_key<
                Result,
                key
              >::type,
              Result,
              typename boost::mpl::insert<
                Result,
                boost::mpl::pair<
                  key,
                  typename boost::mpl::at<
                    Graph,
                    key
                  >::type
                >
              >::type
            >::type next;
          typedef typename clean_graph<
              Graph,
              typename boost::mpl::next<First>::type,
              Last,
              next
            >::type type;
        };
    
        template<typename Graph, typename First, typename Result>
        struct clean_graph<Graph, First, First, Result> {
          typedef Result type;
        };
    

    Diese Funktion löscht Duplikate aus einer Graphenliste. Wird benutzt um Toposort zur Compilezeit zu implementieren. Frag bitte nicht, wozu das gut sein soll, es hat seinen Zweck. Und vielleicht ließe sich das auch eleganter implementieren. Aber es funktioniert, so wie es ist.

    Kein Grund zur Trauer.



  • Mr. N schrieb:

    Wieso ist ein Konstrukt schlechter, weil dus komisch findest?

    es ist zumindest schlechter als ich dachte.
    sorry, ich bin nicht so der erfahrene C++ user und war bisher der meinung, dass mit templates (z.b. function templates) alles was möglich ist, zur compile-time berechnet wird. das scheint aber nicht der fall zu sein und wirkt auf mich deshalb ziemlich ernüchternd...
    🙂



  • Assembler funktioniert auch. Normales C++ is ja schon grausig, aber sowas is einfach nur noch :kotzsmilie:


Anmelden zum Antworten