Aufruf von Templatefunktionen mit Reference Collapsing (Bug?)



  • Hallo zusammen,

    beim Schreiben dieses Programmes (http://ideone.com/uIIPMi) ist mir etwas Seltsames aufgefallen.
    Reduziert auf das Wesentliche:

    class foo
    {
    public:
        foo(const int&) {std::cout << "const int &\n";}
        template <typename T>
        foo(T &&) {std::cout << "T &&\n";}
    };
    
    void bar(const int &) {std::cout << "const int &\n";}
    template <typename T> void bar(T &&) {std::cout << "T &&\n";}
    
    void foobar(const int &) {std::cout << "const int &\n";}
    template <typename T> void foobar(T) {std::cout << "T\n";}
    
    int main()
    {
        foo f(5);
        bar(5);
        foobar(5);
    }
    

    Output:
    T && 😮
    T && 😮
    Aber:
    const int &

    Offenbar scheint der Compiler (GCC 4.8.1) die Templatefunktion aufzurufen, sobald Reference Collapsing im Spiel ist. Sollte es nicht immer zuerst mit einer normalen Funktion probiert werden? Steht das so im Standard?



  • ---



  • Nathan schrieb:

    Bug?

    lol

    Integer-Literale sind prvalues. int&& ist ein besserer match als int const& , deshalb wird die Template-Funktion genommen.



  • Oh, ähm. Ups. 😃
    Das ist mir jetzt peinlich.
    Irgendwie hatte ich Integer Literale nicht als RValues auf dem Schirm.
    Ich sollte mehr schlafen.



  • N3337 §13.3.3.2/3 schrieb:

    Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...] S1 and S2 are reference bindings (8.5.3) [...] and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.



  • Ja, ich brauche mehr Schlaf, aber nciht weil ich zu blöd bin 5 als RValue anzuerkennen, sondern weil ich Probleme nicht richtig reduzieren kann. 🙄
    Die main() sollte eigentlich so aussehen (also mit einem LValue als Aufruf):

    int main()
    {
        int i;
        foo f(i);
        ...
    }
    

    Gleicher Output: http://ideone.com/Xf3qwL
    Und der überrascht mich jetzt.
    i ist definitiv ein LValue, also sollte auch eigentlich die const int & Version aufgerufen werden...?

    Edit: Moment, mache ich aus dem const int & ein int & gehts. Aber selbst wenn das so gewollt ist, ist das nicht ein bisschen - unerwartet?



  • T wird zu int& deduziert, damit wird der Parameter zu int& , und das passt besser.
    http://ideone.com/fEmYgI



  • Danke, Sone, darauf bin ich auch selber gekommen (s. Edit). 🙄 😉
    Trotzdem finde ich das seltsam, dass die Templatefunktion aufgerufen wird.
    imo passt da die non-Template Version mindestens genauso gut, also sollte da wenigstens eine Mehrdeutigkeit sein.



  • Nathan schrieb:

    Danke, Sone, darauf bin ich auch selber gekommen (s. Edit). 🙄 😉

    ?? Ich erkenne in deinem Edit nichts. Musst nicht gleich unfreundlich werden. 🙂

    imo passt da die non-Template Version mindestens genauso gut, also sollte da wenigstens eine Mehrdeutigkeit sein.

    Du findest, int& passt bei einem lvalue nicht besser als int const& , wobei letzeres auch die semantische Eigenschaft hat an ein rvalue gebunden werden zu können? Finde ich unintuitiv.



  • Sone schrieb:

    Nathan schrieb:

    Danke, Sone, darauf bin ich auch selber gekommen (s. Edit). 🙄 😉

    ?? Ich erkenne in deinem Edit nichts. Musst nicht gleich unfreundlich werden. 🙂

    Sone, irgendwelche Aussagen von mir, die mit 😉 abgeschlossen werden, sind nciht ernst gemeint. 🙂
    Und, jetzt wo ich meinen Edit nochmal gelesen habe, sehe ich, dass ich das tatsächlich nicht geschrieben habe, obwohl ich es gedacht habe.

    imo passt da die non-Template Version mindestens genauso gut, also sollte da wenigstens eine Mehrdeutigkeit sein.

    Du findest, int& passt bei einem lvalue nicht besser als int const& , wobei letzeres auch die semantische Eigenschaft hat an ein rvalue gebunden werden zu können? Finde ich unintuitiv.

    Nee, das nicht. Sondern die Templateaufrufregeln, die offenbar besagen, dass sobald da eine Konversion stattfinden müsste, die Templatefunktion aufgerufen wird. Das finde ich nicht gut. Genauer ausgedrückt: Bei einer float zu int Konversion ist das ok, aber nicht bei einem Hinzufügen von einem const - finde ich.
    Naja, ist auch egal, mit enable_if konnte ich mich da rausholen (Was glaub ich auch für solche Dinge gedacht ist).



  • Aufgrund der unter Umständen unerwarteten Resultate beim Überladen von "Universal References" (Scott Meyers) gibt dieser als Teil seines neuen (noch nicht erschienen) Buches "Effective C++11/14" den Ratschlag, dies zu vermeiden

    Er hat zu diesem Thema neulich einen Vortrag gehalten ("The Universal Reference/Overloading Collision Conundrum"). Da allerdings im Video zum Vortrag einige Fehler in den Folien stecken, würde ich empfehlen, direkt die korrigierte Version der Folien anzusehen.


Anmelden zum Antworten