Disambigui il puntatore della funzione membro sovraccarico passato come parametro del template

Sto tentando di ricreare il pattern Observer in cui posso inoltrare perfettamente i parametri a una funzione membro degli osservatori.

Se provo a passare l’indirizzo di una funzione membro che ha più sostituzioni , non è ansible dedurre la funzione membro corretta in base agli argomenti.

#include  #include  #include  template struct observer_list { template void call(Ret (Class::*func)(Args...), UArgs&&... args) { for (auto obj : _observers) { (obj->*func)(std::forward(args)...); } } std::vector _observers; }; struct foo { void func(const std::string& s) { std::cout << this << ": " << s << std::endl; } void func(const double d) { std::cout << this << ": " << d << std::endl; } }; int main() { observer_list l; foo f1, f2; l._observers = { &f1, &f2 }; l.call(&foo::func, "hello"); l.call(&foo::func, 0.5); return 0; } 

Questo non riesce a compilare con template argument deduction/substitution failed .

Si noti che ho avuto Args... e UArgs... perché devo essere in grado di passare parametri che non sono necessariamente dello stesso tipo come il tipo di firma della funzione, ma sono convertibili in tale tipo.

Stavo pensando che potrei usare una std::enable_if<std::is_convertible> per disambiguare, ma non credo di poterlo fare con un pacchetto di parametri template variadic?

    Come posso ottenere che la deduzione degli argomenti del modello funzioni qui?

    Il problema è qui:

     l.call(&foo::func, "hello"); l.call(&foo::func, 0.5); 

    Per entrambe le linee, il compilatore non sa a quale foo::func ti riferisci. Quindi, devi disambigarti fornendo le informazioni sul tipo che mancano (cioè il tipo di foo:func ) attraverso i cast:

     l.call(static_cast(&foo::func), "hello"); l.call(static_cast(&foo::func), 0.5); 

    In alternativa, puoi fornire gli argomenti del modello che il compilatore non può dedurre e che definiscono il tipo di func :

     l.call(&foo::func, "hello"); l.call(&foo::func, 0.5); 

    Nota che devi usare double e non const double sopra. Il motivo è che generalmente double e const double sono due tipi diversi. Tuttavia, c’è una situazione in cui double e const double sono considerati come se fossero dello stesso tipo: come argomenti di funzione. Per esempio,

     void bar(const double); void bar(double); 

    non sono due diversi sovraccarichi ma sono in realtà la stessa funzione.