Desambigua o ponteiro de function de membro sobrecarregado sendo passado como parâmetro de modelo

Eu estou tentando recriar o padrão Observer, onde posso perfeitamente encaminhar parâmetros para uma determinada function de membro dos observadores.

Se eu tentar passar o endereço de uma function de membro que tenha várias substituições , ele não poderá deduzir a function de membro correta com base nos argumentos.

#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; } 

Isso não consegue compilar com a template argument deduction/substitution failed .

Note que eu tinha Args... e UArgs... porque eu preciso ser capaz de passar parâmetros que não são necessariamente do mesmo tipo que o tipo da assinatura da function, mas são conversíveis para o tipo.

Eu estava pensando que eu poderia usar uma chamada std::enable_if<std::is_convertible> para desambiguar, mas eu não acredito que posso fazer isso com um pacote de parâmetro de modelo variadic?

Como posso obter a dedução de argumento de modelo para trabalhar aqui?

A questão está aqui:

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

Para ambas as linhas, o compilador não sabe a qual foo::func você está se referindo. Portanto, você deve se desambiguar fornecendo as informações de tipo que estão faltando (ou seja, o tipo de foo:func ) através de cast:

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

Como alternativa, você pode fornecer os argumentos de modelo que o compilador não pode deduzir e que definem o tipo de func :

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

Observe que você tem que usar double e não const double acima. A razão é que geralmente double e const double são dois tipos diferentes. No entanto, há uma situação em que double e const double são considerados como se fossem do mesmo tipo: como argumentos de function. Por exemplo,

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

não são duas sobrecargas diferentes, mas são na verdade a mesma function.