C++0x variadic templates Elemente zur Runtime



  • hustbaer schrieb:

    krümelkacker schrieb:

    hustbaer schrieb:

    @camper:
    Sagmal, kann man in C++0x eigentlich auch nach constexpr überladen?

    Ich versteh die Motivation noch nicht ganz. Du würdest also eine Funktion gern 2mal implementieren (statt 1mal) weil die zweite, überflüssige Implementierung hübscher aussieht?

    Nein. Sondern weil die zweite, überflüssige Implementierung schneller läuft. Was mir compiletime (relativ) wurscht ist, runtime aber nicht unbedingt.

    Dann verzichtet man aber auf die Garantie, dass die Funktion, egal ob zur Compiletime oder Runtime berechnet, immer das selbe Ergebnis hat. Das verspricht schöne Debugsitzungen, wenn man in einer der beiden Implementeirungen einen Bug hat und foo(i) sich plötzlich unterschiedlich verhält, sobald der Compiler den Wert für i vorher weiß. Verglichen mit dem relativ geringem Nutzen (wenn man einen bestimmten Wert unbedingt zur Compilezeit braucht, tut ja eine andere Funktion wirklich nicht weh) finde ich die jetzige Lösung besser.



  • ipsec schrieb:

    Dann verzichtet man aber auf die Garantie, dass die Funktion, egal ob zur Compiletime oder Runtime berechnet, immer das selbe Ergebnis hat. Das verspricht schöne Debugsitzungen, wenn man in einer der beiden Implementeirungen einen Bug hat

    Davor möchte ich aber nicht beschützt werden, wenn ich dafür Laufzeitkosten zahlen muß.


  • Mod

    Von einer derartigen transparenten Überladung der gleichen Funktion halte wenig, eben genau deshalb weil es prinzipiell nicht feststellbar ist, welche Funktion aufgerufen wird, zudem kriegt man dafür auch kaum eine entsprechend brauchbare ODR-Regel formuliert.
    Mein Ansatz war anders gedacht: ein constexpr als Teil eines Parameters wirkt einschränkend, so dass das Argument für den jeweiligen Parameter ein konstanter Ausdruck sein muss. Die Überladungsauflösung könnte analog zu den üblichen const/non-const Überladungen funktionieren.
    Dann könnte man z.B. haben

    void foo(constexpr int, constexpr int); // (1)
    void foo(int, constexpr int);  // (2)
    
    int a = 1;
    foo(1, 1); // ruft (1) auf
    foo(a, 1); // ruft (2) auf
    foo(a, a); // ill-formed
    


  • volkard schrieb:

    ipsec schrieb:

    Dann verzichtet man aber auf die Garantie, dass die Funktion, egal ob zur Compiletime oder Runtime berechnet, immer das selbe Ergebnis hat. Das verspricht schöne Debugsitzungen, wenn man in einer der beiden Implementeirungen einen Bug hat

    Davor möchte ich aber nicht beschützt werden, wenn ich dafür Laufzeitkosten zahlen muß.

    Musst du ja nicht. Nichts hindert dich, die Funktion zu optimieren. Dann geht eben nur constexpr nicht mehr und du must dafür eine neue Funktion nehmen (oder gleich eine Metafunktion) - wo ist das Problem? Relevant wird das ganze ja eigentlich nur, wenn sich eine Funktion als Performancefresser herausstellt und unbedingt zur Compilezeit berechnet werden muss. Dann hilft dir constexpr auch nicht weiter, das ist zwar ein Hinweis an den Compiler, dass die Funktion zur Compilezeit berechnet werden kann, aber kein Zwang, das wenn möglich tatsächlich zu tun.
    Dann also doch gleich eine Metafunktion (oder eben eine zusätzliche constexpr -Variante und auf den Compiler hoffen) und man hat zusätzlich noch eine Dokumentation, dass im konkreten Fall eine Berechnung zur Compiletime erwünscht ist.

    Ich sehe auch durchaus Vorteile, würde der Compiler das automatisch machen. Aber dann sehe ich eben schwer zu findende Bugs bei unerheblich weniger Aufwand.



  • camper schrieb:

    foo(a, 1); // ruft (2) auf
    foo(a, a); // ill-formed
    

    Prinzipiell könnte in diesem Fall der Compiler beide Male (1) aufrufen, da a sich ja nicht ändert. Mit solchen Regeln würde man den Compiler also nur in seiner Optimierungsfreiheit beschränken. Und davon abgesehen: welchen genauen Vorteil hast du in foo , wenn du weißt, dass bestimmte Parameter compiletime-konstante Werte sind?


  • Mod

    ipsec schrieb:

    camper schrieb:

    foo(a, 1); // ruft (2) auf
    foo(a, a); // ill-formed
    

    Prinzipiell könnte in diesem Fall der Compiler beide Male (1) aufrufen, da a sich ja nicht ändert. Mit solchen Regeln würde man den Compiler also nur in seiner Optimierungsfreiheit beschränken.

    Wieso? Er kann ja immer noch versuchen, foo-2 inline zur COmpilezeit auszuwerten, wenn dessen Code das hergibt.

    ipsec schrieb:

    Und davon abgesehen: welchen genauen Vorteil hast du in foo , wenn du weißt, dass bestimmte Parameter compiletime-konstante Werte sind?

    Nun, einmal kann man eine Überladung so wie von hustbaer gewünscht erreichen, dann macht man die Funktion selbst noch constexpr.
    Eine andere Optimierungsmöglichkeit wäre z.B. gegeben, wenn assembler im Spiel ist, ist ein bestimmter Parameter ein konstanter Ausdruck, kann man ggf. Register einsparen und mit immediates arbeiten (wobei mir durchaus bewusst ist, dass es dafür bereits Möglichkeiten gibt, je nach Eigenschaften der Parameter alternativen Code anzubieten).
    Abgeshen davon kommt mir das Ganze so etwas sauberer vor; ist constexpr Teil des Typ muss z.B. nicht groß überlegen, wass passiert wenn man die Funktion über einen Zeiger oder z.B. ein std::function-Objekt benutzt.



  • camper schrieb:

    Von einer derartigen transparenten Überladung der gleichen Funktion halte wenig, eben genau deshalb weil es prinzipiell nicht feststellbar ist, welche Funktion aufgerufen wird, zudem kriegt man dafür auch kaum eine entsprechend brauchbare ODR-Regel formuliert.

    Hm.
    Hmmmmmm.
    Also ich glaub' dir das jetzt einfach mal, auch wenn ich mir nicht ganz sicher bin was genau das Problem wäre.
    Alles unter einen Hut zu bringen (ODR, Funktionszeiger, Overload-Resolution etc.) könnte auf jeden Fall durchaus schwierig werden.

    Mein Ansatz war anders gedacht: ein constexpr als Teil eines Parameters wirkt einschränkend, so dass das Argument für den jeweiligen Parameter ein konstanter Ausdruck sein muss. Die Überladungsauflösung könnte analog zu den üblichen const/non-const Überladungen funktionieren.

    Hm. Das heisst, das ginge dann auch nicht:

    constexpr int foo(constexpr int);
    
    int (*fp)(int) = foo; // error: no conversion from [constexpr int (*)(constexpr int)] to [int (*)(int)]
    


  • camper schrieb:

    template <unsigned... Indices>
    struct Foo
    {
        static constexpr unsigned indices[] = { Indices... };
        static constexpr unsigned IndexAtI(std::size_t i) { return indices[i]; }
    };
    

    bzw.

    template <unsigned... Indices>
    struct Foo
    {
        static const unsigned indices[sizeof... Indices];
        static constexpr unsigned IndexAtI(std::size_t i) { return indices[i]; }
    };
    
    template <unsigned... Indices>
    const unsigned Foo<Indices...>::indices[] = { Indices... };
    

    solange g++ constexpr noch nicht vollständig umsetzt.

    Du hast recht, ich kann das obere noch nicht übersetzten. Aber laut GCC Seite ist es implementiert: http://gcc.gnu.org/projects/cxx0x.html. Ich arbeite mit GCC 4.6, was haben die denn verbockt 😞


Anmelden zum Antworten