2013-02-27 10 views
7

próbowałem skompilować następujący przykład używając VC11 i g ++ 4.7.2:Jak mogę powiązać szablon funkcji członek przechodzącego nieokreślone połączenia opakowanie

#include <functional> 

class X { 
public: 
    template <typename T> 
    explicit X(T t) 
    { 
    std::bind(&X::invoke<T>, this, t)(); 
    } 
private: 
    template <typename T> 
    void invoke(T t) 
    { 
    t(); 
    } 
}; 

class Y { 
    public: 
    void foo() { 
     //... 
    } 
}; 


int main() { 
    Y y; 
    X x(std::bind(&Y::foo, &y)); 
    return 0; 
} 

ale skończył z błędami. Nie jestem pewien, czy to jest rozsądne, aby wkleić cały wyjście kompilatorów ale generalnie

vc11 mówi:

error C2664: 'void std::_Pmf_wrap::operator()(_Farg0 &,_V0_t) const' : cannot convert parameter 3 from 'void' to 'std::_Bind,Y *,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>' c:\program files (x86)\microsoft visual studio 11.0\vc\include\functional 1152 1 ConsoleApplication1 (Microsoft Visual C++ Compiler Nov 2012 CTP)

i g ++:

Compilation finished with errors:
source.cpp: In instantiation of 'X::X(T) [with T = std::_Bind(Y*)>]':
source.cpp:28:33: required from here
source.cpp:8:9: error: no match for call to '(std::_Bind_helper(Y*)>), X* const, std::_Bind(Y*)>&>::type {aka std::_Bind(Y*)>)>(X*, std::_Bind(Y*)>)>})()'

Czy istnieje sposób, aby rozwiązać ten problem. Bardzo ważne jest dla mnie, aby zapisać główną ideę - klasę, która może być utworzona z dowolnym obiektem wywoływanym (obiekt funkcji, wskaźnik funkcji lub opakowanie zawijania zwrócone przez funkcję std::bind()).

Byłbym wdzięczny, gdyby ktoś pomógł.

P.S. Kompiluje się, jeśli utworzę instancję X, przekazując obiekt funkcji lub wskaźnik funkcji.

Odpowiedz

2

Przyczyną problemu wydaje się wewnętrzne kopiowanie jego argumentów wykonanych przez std::bind, ze szczególnym uwzględnieniem t.

Możesz obejść to w ten sposób:

template <typename T> 
    explicit X(T t) 
    { 
     std::bind(&X::invoke<T>, this, std::placeholders::_1)(t); 
     //        ^^^^^^^^^^^^^^^^^^^^^^
    } 

ta będzie również pracować, ale nie będzie mógł dokonać wyniku bind przeżyć argument t (w przeciwnym razie można byłoby przepuszczenie zwisające odniesienie do invoke<T>()):

template <typename T> 
    explicit X(T t) 
    { 
     std::bind(&X::invoke<T>, this, cref(t))(); 
     //        ^^^^^^^ 
    } 

UPDATE:

Powyższe rozwiązania są obejściami, które pomagają osiągnąć to, co pokazujesz w swoim przykładzie. Jednak z komentarzy wynika, że ​​Twój przypadek użycia może być całkiem inny (np. Pominięcie wyniku z bind dla późniejszej oceny).

Jak słusznie zauważył w swojej odpowiedzi Maxim Yegorushkin, koncepcyjnie poprawne rozwiązanie polega na użyciu konstruktu, takiego jak Boost's protect.

W przypadku, gdy nie chcesz używać Boost jest to dość łatwe do zdefiniowania własnych protect() funkcyjnych za pomocą C++ 11 w zmiennej liczbie argumentów szablony:

// Imitates boost::protect, but with variadic templates and perfect forwarding 
namespace detail 
{ 
    template<typename F> 
    struct protect 
    { 
    private: 

     F _f; 

    public: 

     explicit protect(F f): _f(f) 
     { 
     } 

     template<typename... Ts> 
     auto operator() (Ts&&... args) -> 
      decltype(_f(std::forward<Ts>(args)...)) 
     { 
      return _f(std::forward<Ts>(args)...); 
     } 
    }; 
} 

template<typename F> 
detail::protect<F> protect(F&& f) 
{ 
    return detail::protect<F>(std::forward<F>(f)); 
} 

Ostatecznie, to jak można używać go w klasa, jak Maxim zaproponował:

class X 
{ 
public: 
    template <typename T> 
    explicit X(T t) 
    { 
     auto pt = protect(t); 
     std::bind(&X::invoke<decltype(pt)>, this, pt)(); 
    } 
private: 
    template <typename T> 
    void invoke(T t) 
    { 
     t(); 
    } 
}; 
+0

Rozwiązuje to nieco inny problem, według mnie. Oryginalne wyrażenie wiążące jest samoistne w tym sensie, że przechowuje kopie wszystkich argumentów, tak że argumenty mogą zostać zniszczone. Twoje rozwiązania pomijają kopiowanie funktora i polegają na tym, że wciąż jest on w zasięgu i żyje w momencie połączenia. Realistycznie nie ma sensu tworzenie wyrażenia powiązania, a następnie natychmiastowe wywoływanie go. Zamiast tego chcesz przekazać funktorowi utworzony przez bindowanie gdzie indziej i często opóźniając wywoływanie do później. –

+0

@MaximYegorushkin: Rzeczywiście. Jest to jednak skuteczne obejście problemu użycia OP. –

+0

Nie jestem pewien, czy to jest jego prawdziwy przypadek użycia. Zobacz mój zaktualizowany poprzedni komentarz. –

4

myślę, że pominięto ważną bitowej boost::bind podczas przyjmowania go do std::bind, mianowicie boost::protect().Kod może być ustalona tak:

#include <boost/bind/protect.hpp> 
// ... 
X x(boost::protect(std::bind(&Y::foo, &y))); 

lub, alternatywnie:

template <typename T> 
explicit X(T t) 
{ 
    auto tt = boost::protect(t); 
    auto f = std::bind(&X::invoke<decltype(tt)>, this, tt); 
    f(); 
} 

Zobacz http://www.boost.org/doc/libs/1_53_0/libs/bind/bind.html

Although the first argument is, by default, not evaluated, all other arguments are. Sometimes it is necessary not to evaluate arguments subsequent to the first, even when they are nested bind subexpressions. This can be achieved with the help of another function object, protect, that masks the type so that bind does not recognize and evaluate it. When called, protect simply forwards the argument list to the other function object unmodified.

The header boost/bind/protect.hpp contains an implementation of protect. To protect a bind function object from evaluation, use protect(bind(f, ...)).


Nadchodzący Effective C++11: Content and Status przez Scott Meyers zamierza zarekomendować preferuj lambdas na std :: bind. W C++ 11 możesz po prostu:

template <typename T> 
explicit X(T t) 
{ 
    auto f = [t, this]() { this->invoke(t); }; 
    f(); 
} 
// ... 

X x([&y](){ y.foo(); }); 
+0

Nadchodzące Efektywne C++ 11: Treść i Status są dostępne jako "Efektywny Współczesny C++". Pozycje 34 to "preferuj lambdas to std :: bind", a kończy się "Lamda jest bardziej czytelna, bardziej ekspresyjna ...". –

+0

@JeanDavy Czy nie wydano go kilka miesięcy temu? –

+0

Oup, tak, jest. W ostatnich miesiącach pracowałem z Javą i Pythonem, więc zaniedbałem C++, teraz wróciłem do mojej pierwszej prawdziwej miłości ;-), właśnie to kupiłem. –

Powiązane problemy