Template-Parameter: Übergabe per Wert oder Referenz?



  • hustbaer schrieb:

    Das nennt sich das "forwarding problem", und es gibt in C++ keine (praktikable) Lösung.

    sicher gibt es praktikable lösungen - nur ideale gibts nicht.

    foo<int_holder> x(int_holder(42));
    
        blah b;
        foo<blah_holder> y(blah_holder(b));
    

  • Administrator

    Meine Lösung bezog sich auch nur auf das Beispiel, also für einen Parameter. Für variable Parameter entweder die Lösung von Shade Of Mine nehmen, mehrfach Überladungen machen, wie es in Boost der Fall ist (Boost.Preprocessor könnte hilfreich sein) oder auf C++0x warten. Variadic Templates sollten das Problem wohl lösen.

    template <typename T>
    class foo
        : public T
    {
    public:
        template <typename ...A>
        foo(A& ...a) : T(a...) { }
    
        template <typename A>
        foo(A const& ...a) : T(a...) { }
    };
    

    Grüssli



  • Shade Of Mine schrieb:

    sicher gibt es praktikable lösungen - nur ideale gibts nicht.

    foo<int_holder> x(int_holder(42));
    
    blah b;
    foo<blah_holder> y(blah_holder(b));
    

    Der Ansatz funktioniert bei mir leider nicht, da einige der in foo enthaltenen Klassen nicht kopierbar sind. Ich muß also schon direkt das foo<T> konstruieren können 😞

    Dravere schrieb:

    Variadic Templates sollten das Problem wohl lösen.

    Aber auch erst in Verbindung mit Rvalue-References, oder? Also in etwa so:

    template <typename ...A>
    foo(A && ...a) : T(a...) { }
    

    Wenn der neue Standard nur endlich mal fertig würde...

    Boost.Preprocessor habe ich schon einige Male erfolgreich benutzt, um damit quasi Variadic Templates zu simulieren. Aber wie kann ich mir denn alle möglichen Kombinationen von "A & a" und "A const & a" erzeugen lassen?


  • Administrator

    dooooomi schrieb:

    Aber wie kann ich mir denn alle möglichen Kombinationen von "A & a" und "A const & a" erzeugen lassen?

    Ehm, moment, willst du auch Kreuzkombinationen zulassen? Also zum Beispiel sowas:

    template <typename T>
    class foo
        : public T
    {
    public:
        template <typename A, typename B>
        foo(A& a, B const& b) : T(a...) { }
    
        template <typename A, typename B>
        foo(A const& a, B& b) : T(a...) { }
    };
    

    Ich dachte bisher, dass alle Werte per Value oder alle Werte per Referenz weitergegeben würden. Aber so ist das natürlich ... ufff
    Das gibt eine Menge an Kombinationsmöglichkeiten 😃

    Kannst du das nicht ein wenig einschränken?
    Ansonsten, wie wäre es mit einem Passthrough Pattern oder wie man dem Ding sagt *lol*

    template<typename T>
    class foo
        : public T
    {
    public:
        foo(typename T::ConstrutorParams const& params) : T(params) { }
    };
    

    Jeder Typ hat somit eine Struktur, welche die Parameter für den Konstruktor aufnimmt.

    Grüssli



  • Dravere schrieb:

    Aber so ist das natürlich ... ufff
    Das gibt eine Menge an Kombinationsmöglichkeiten 😃

    So ist es... 🙄

    Einschränken kann ich das nicht wirklich, da das ganze mit beliebigen Typen für T funktionieren soll. Auch die "Passthrough" Variante fällt somit leider weg, da ich dafür ja die jeweiligen Klassen um einen zusätzlichen Konstruktor erweitern müßte.

    Boost.Preprocessor wäre für mich eine akzeptable Lösung, solange sich dadurch die Compile-Zeiten nicht allzu extrem verlängern. Und wenn ich beispielsweise für bis zu 10 Parameter entsprechende Konstruktoren erzeugen würde, dann wären das ja auch "nur" 2047 Kombinationen 🙂

    Aber wie gesagt, mit Boost.Preprocessor weiß ich derzeit nicht wirklich weiter...


  • Administrator

    Ok, dann krame ich halt noch die letzte Möglichkeit aus. Diese in Kombination mit Boost.Preprocessor dürfte wohl fast am einfachsten sein:

    template<typename T>
    class Foo
        : public T
    {
    public:
        template<typename T1>
        Foo(T1 p1) : T(p1) { }
    
        template<typename T1, typename T2>
        Foo(T1 p1, T2 p2) : T(p1, p2) { }
    
        // usw. mit Boost.Preprocessor oder von Hand, wie du willst.
    };
    
    // Verwendung:
    int a = 0;
    int b = 0;
    
    Foo<XYZ> f1(a, b); // Alle per Value
    Foo<XYZ> f2(boost::ref(a), b); // Erste per Referenz, zweite per Value
    Foo<XYZ> f3(boost::ref(b)); // Einmal per Referenz
    

    Usw., wie du es halt gerne möchtest. Es wird also alles per Value übergeben und eine Wrapperklasse für Referenzen genutzt, welche implizit in eine "normale" Referenz umgewandelt werden kann. Boost.Bind macht dies, glaub ich, auch so.

    Grüssli



  • Dravere schrieb:

    Ok, dann krame ich halt noch die letzte Möglichkeit aus. Diese in Kombination mit Boost.Preprocessor dürfte wohl fast am einfachsten sein:

    ...
    
    Foo<XYZ> f1(a, b); // Alle per Value
    Foo<XYZ> f2(boost::ref(a), b); // Erste per Referenz, zweite per Value
    Foo<XYZ> f3(boost::ref(b)); // Einmal per Referenz
    

    Usw., wie du es halt gerne möchtest. Es wird also alles per Value übergeben und eine Wrapperklasse für Referenzen genutzt, welche implizit in eine "normale" Referenz umgewandelt werden kann. Boost.Bind macht dies, glaub ich, auch so.

    Danke, die Lösung finde ich gar nicht schlecht. Daß der Aufrufer des Konstruktors in diesem Fall wissen muß, wann boost::ref nötig ist, damit kann ich leben.

    Wenn natürlich noch jemand eine Idee hätte, wie man sämtliche nötigen Overloads (mit konstanten und nicht-konstanten Referenzen) per Boost.Preprocessor erzeugen könnte, würde mich das nach wie vor sehr interessieren.



  • Shade Of Mine schrieb:

    hustbaer schrieb:

    Das nennt sich das "forwarding problem", und es gibt in C++ keine (praktikable) Lösung.

    sicher gibt es praktikable lösungen - nur ideale gibts nicht.

    Lösung des "forwarding problem" impliziert für mich vollständig/ideal. Das ist natürlich Definitionssache, aber so habe ich es gemeint.

    Und die einzige (vollständige) Lösung die es gibt, ist eben nicht praktikabel.

    So war das zu verstehen.

    Natürlich gibt es mehrere Möglichkeiten es irgendwie zu machen, die sind in dem von mir verlinkten Paper eh alle angeführt (zumindest alle relevanten) - inklusive Vor- und Nachteile.



  • dooooomi schrieb:

    Wenn natürlich noch jemand eine Idee hätte, wie man sämtliche nötigen Overloads (mit konstanten und nicht-konstanten Referenzen) per Boost.Preprocessor erzeugen könnte, würde mich das nach wie vor sehr interessieren.

    Das würde ich garnicht erst versuchen. Das würde dir die Compile-Zeiten extrem hochjagen. Angenommen du willst z.B. 10 Parameter zulassen, wären das schon 1024 Overloads. Bei jedem Aufruf müsste der Compiler dann "das beste" aus 1024 Funktionstemplates aussuchen.

    Wenn ihr gratis Kaffee in der Firma habt, und du gerne erzwungene Pausen machst, dann mach es so 😃



  • hustbaer schrieb:

    dooooomi schrieb:

    Wenn natürlich noch jemand eine Idee hätte, wie man sämtliche nötigen Overloads (mit konstanten und nicht-konstanten Referenzen) per Boost.Preprocessor erzeugen könnte, würde mich das nach wie vor sehr interessieren.

    Das würde ich garnicht erst versuchen. Das würde dir die Compile-Zeiten extrem hochjagen. Angenommen du willst z.B. 10 Parameter zulassen, wären das schon 1024 Overloads. Bei jedem Aufruf müsste der Compiler dann "das beste" aus 1024 Funktionstemplates aussuchen.

    Wenn ihr gratis Kaffee in der Firma habt, und du gerne erzwungene Pausen machst, dann mach es so 😃

    Leider hast du völlig Recht. Ich habe mir einmal die Overloads für 10 Parameter erzeugt (nicht mit Boost.Preprocessor, sondern mittels eines kleinen Python-Scripts). Da hat der Compiler schon kräftig dran zu arbeiten. Und wenn ich mir dann vorstelle, wie das erst sein soll, wenn der Code nicht "fertig" generiert wäre, sondern erst vom Präprozessor erzeugt würde, der ja auch nie für sowas gedacht war...

    Mit einer kleineren Anzahl von Parametern (konkret brauche ich derzeit maximal 7) wird's natürlich deutlich schneller, aber eine wirkliche Lösung ist das ja auch nicht. Dann bleibe ich lieber bis auf Weiteres bei Dravere's letztem Vorschlag, und freue mich schonmal auf C++0x, wo das alles dank Variadic Templates und Rvalue-References dann in einer einzigen Zeile geht 🙂


Anmelden zum Antworten