bind und rvalue-Referenzen



  • Hiho,

    meine Frage möchte ich an folgendem Codebeispiel erläutern:

    std::function<void(CSystemMessage&&, CMessageChannel*)> handler = std::bind(&CMessageServer::handleSystemMessage, this, std::placeholders::_1, std::placeholders::_2);
    

    oder mit boost:

    boost::function<void(CSystemMessage&&, CMessageChannel*)> handler = boost::bind(&CMessageServer::handleSystemMessage, this, _1, _2);
    

    Wie man an der functtion sieht, soll ein Parameter ein rvalue sein. Die Methode handleSystemMessage hat genau die gewünschte Signatur:

    void handleSystemMessage(CSystemMessage&& msg, CMessageChannel* pChannel);
    

    Das ganze endet in einer ziemlich langen Fehlermeldung vom Compiler. Hier nur ein kurzer Ausszug:

    D:\libraries\cpp\boost\boost_1_55_0\boost/bind/bind.hpp(392): error C2664: 'void boost::_mfi::mf2<void,tuc::nw::CMessageServer,tuc::nw::CSystemMessage &&,tuc::nw::CMessageChannel *>::operator ()(T *,A1,A2) const' : Konvertierung von Argument 2 von 'tuc::nw::CSystemMessage' in 'tuc::nw::CSystemMessage &&' nicht möglich

    Mache ich prinzipiell was falsch, oder kann bind das nicht, oder ist das ein Comnpiler/Bibliotheksproblem?

    VG

    Pellaeon


  • Mod

    Der Code* ist mit** std:: ** bind korrekt, weil bind für die Argumente perfect forwarding anwendet. Und ich bin mir sicher, dass Boost auch perfect forwarding nutzen sollte.

    Sieht nach der Fehlermeldung zu urteilen eher nach einem VC++-Bug aus.

    ~* Edit: Ich kenne den Code nicht, denke aber du definierst/verwendest alles entsprechend.~



  • Ob std::bind das prinzipiell können soll (Argumente richtig forwarden), kann ich gerade nicht mit Sicherheit sagen. Da müsste ich mal im Standard nachgucken. Aber ich wäre stark überrascht, wenn das nicht klappen sollte. In dem Fall wäre die Standardbibliothek und/oder Compiler noch nicht auf dem aktuellen Stand. Boost.Bind würde ich das weniger zutrauen.

    ich frage mich aber, warum Du überhaupt noch bind verwendest, wenn Du doch sowieso schon C++11 mit Rvalue-Referenzen und damit auch Lambdas voraussetzt.

    Probier mal:

    std::function<void(CSystemMessage&&, CMessageChannel*)> handler =
       [this](CSystemMessage&& m, CMessageChannel* c) {
          this->handleSystemMessage(std::move(m),c);
       };
    


  • kkaw schrieb:

    ich frage mich aber, warum Du überhaupt noch bind verwendest, wenn Du doch sowieso schon C++11 mit Rvalue-Referenzen und damit auch Lambdas voraussetzt.

    Ob bind oder Lambda ist ja letztendlich Geschmackssache. Sollte beides zum Ziel führen. Wenn ich wirklich nur einen Aufruf durchleiten will, nehme ich einfach aus Gewohntheit bind. Sollen noch zusätzliche Aktionen/Befehle ausgeführt werden, dann wirds natürlich ein Lambda.

    Hier nochmal ein Minimalbeispiel ohne meine Klassen:

    // BindTest.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
    //
    
    #include "stdafx.h"
    
    //std includes
    #include <iostream>
    #include <functional>
    
    //boost includes
    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    
    struct Foo
    {
    	void foo(int&& i) { std::cout << i << std::endl; }
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	Foo myFoo;
    
    	//boost -> compile error
    	boost::function<void(int&&)> boostHandler = boost::bind(&Foo::foo, &myFoo, _1);
    
    	//std -> compile error
    	std::function<void(int&&)> stdHandler = std::bind(&Foo::foo, &myFoo, std::placeholders::_1);
    
    	//lambda handler -> works fine
    	std::function<void(int&&)> lambdaHandler = [&myFoo](int&& i) { myFoo.foo(std::move(i)); };
    
    	return 0;
    }
    

    std wie auch die boost-variante gehen bei mir im VS 2013 nicht.


  • Mod

    Ohne Boost kompiliert das fehlerfrei - also falsche Implementierung in VC++'s Standardbibliothek. Kein Perfect Forwarding. Denn ohne Perfect Forwarding wird natürlich einfach ein lvalue übergeben:

    R operator()(T * p, A1 a1) const
        {
            BOOST_MEM_FN_RETURN (p->*f_)(a1); // a1 ist *immer* ein lvalue, auch wenn A1 eine rvalue-Referenz ist.
        }
    

    Ich habe damit die Ursache für diese scheinbar unsinnige Fehlermeldung gefunden: Der Compiler beschwert sich, dass ein lvalue vom Typ int in int&& konvertiert werden will. Das mit dem lvalue sagt aber nur GCC, Clang hat mich da verwirrt.



  • Warum verwendest du überhaupt eine rvalue-Referenz? Das macht man eigentlich nicht, wenn kein Template vorliegt.



  • Nimm das Objekt per LValue-Referenz entgegen.



  • Ist ein alter Bug, den ich mal entdeckt hatte. Der wird wohl erst in der kommenden VS-Version gefixt:
    https://connect.microsoft.com/VisualStudio/feedback/details/717188/bug-concerning-rvalue-references-and-std-function-std-bind-in-vc11



  • Und keine C-Praefixe.


Anmelden zum Antworten