2012-03-19 34 views
17

Rozważmy następujący kod:rodzajowe wskaźnik funkcji członka jako parametr szablonu

#include <iostream> 
using namespace std; 

class hello{ 
public: 
    void f(){ 
     cout<<"f"<<endl; 
    } 
    virtual void ff(){ 
     cout<<"ff"<<endl; 
    } 
}; 

#define call_mem_fn(object, ptr) ((object).*(ptr)) 

template<R (C::*ptr_to_mem)(Args...)> void proxycall(C& obj){ 
    cout<<"hello"<<endl; 
    call_mem_fn(obj, ptr_to_mem)(); 
} 

int main(){ 
    hello obj; 
    proxycall<&hello::f>(obj); 
} 

Oczywiście to nie będzie kompilować na linii 16, ponieważ kompilator nie wie, co R, C i Args są. Ale jest inny problem: jeśli ktoś próbuje określić te parametry szablonu tuż przed ptr_to_mem, wpada tej złej sytuacji:

template<typename R, typename C, typename... Args, R (C::*ptr_to_mem)(Args...)> 
          // ^variadic template, but not as last parameter! 
void proxycall(C& obj){ 
    cout<<"hello"<<endl; 
    call_mem_fn(obj, ptr_to_mem)(); 
} 

int main(){ 
    hello obj; 
    proxycall<void, hello, &hello::f>(obj); 
} 

Niespodziewanie, g ++ nie narzekają Args nie jest ostatni parametr w liście szablonów, ale w każdym razie nie może powiązać proxycall z odpowiednią funkcją szablonu i po prostu zauważa, że ​​jest to potencjalny kandydat.

Jakieś rozwiązanie? Ostatnią opcją jest przekazanie wskaźnika funkcji członka jako argumentu, ale gdybym mógł przekazać go jako parametr szablonu, lepiej by pasował do reszty mojego kodu.

EDYCJA: jak niektórzy zwracają uwagę, przykład wydaje się bezcelowy, ponieważ proxycall nie przejdzie żadnego argumentu. Nie jest tak w prawdziwym kodzie, nad którym pracuję: argumenty są pobierane za pomocą niektórych szablonowych sztuczek ze stosu Lua. Ale ta część kodu jest nieistotna dla pytania, a raczej długa, więc nie wkleję go tutaj.

+0

Wydaje mi się, że w tym przypadku nie potrzebujesz parametrów variadic template. 'proxycall()' nie przekazuje żadnych argumentów do wywołania wskaźnika funkcji członka, więc użycie parametrów szablonu variadic sprawia, że ​​problem jest trudniejszy niż musi. –

+1

To nie ma sensu. Twój "call_mem_fn" #define w rzeczywistości nie dostarcza parametrów. Więc nie zadziała, jeśli Args nie jest puste. Jak więc można się spodziewać, że to rzeczywiście będzie funkcjonować? –

+0

Kod w pytaniu to tylko przykład. Rzeczywisty kod będzie obsługiwał funkcje z dowolną liczbą argumentów i będą one pobierane z innego miejsca (a mianowicie ze stosu Lua). Kod kleju metaprogramowania, który pobiera argumenty, już działa, nie wkleję go tutaj, ponieważ jest długotrwały. –

Odpowiedz

32

Można spróbować czegoś takiego:

template <typename T, typename R, typename ...Args> 
R proxycall(T & obj, R (T::*mf)(Args...), Args &&... args) 
{ 
    return (obj.*mf)(std::forward<Args>(args)...); 
} 

Zastosowanie: proxycall(obj, &hello::f);

Alternatywnie, aby PTMF do szablonu argumentu, spróbuj specjalność:

template <typename T, T> struct proxy; 

template <typename T, typename R, typename ...Args, R (T::*mf)(Args...)> 
struct proxy<R (T::*)(Args...), mf> 
{ 
    static R call(T & obj, Args &&... args) 
    { 
     return (obj.*mf)(std::forward<Args>(args)...); 
    } 
}; 

Zastosowanie:

hello obj; 

proxy<void(hello::*)(), &hello::f>::call(obj); 

// or 

typedef proxy<void(hello::*)(), &hello::f> hello_proxy; 
hello_proxy::call(obj); 
+0

Zawsze zapominam, że gdy mam kłopoty z szablonami funkcji, szablony klas są moją łodzią ratunkową. –

+0

@LorenzoPistone: Zazwyczaj zaimplementujesz swoją magię za pomocą szablonu klasy i korzystasz ze specjalizacji, a na końcu dodajesz mały szablon * funkcji * dedukcji typu, dzięki czemu nie musisz przeliterować parametrów szablonu. W tym przypadku jednak nie wiem, jak to pomaga, ale może to wystarczy, aby dać ci pomysł. –

+1

Nie zapomnij poradzić sobie z funkcjami składowymi 'const'. (Ale jeśli zdecydujesz się nie przejmować 'volatile' lub' const volatile', nie będę narzekać). – aschepler

Powiązane problemy