Disambiguate перегруженный указатель функции участника передается как параметр шаблона
Я пытаюсь воссоздать шаблон Наблюдателя, где я могу идеально перенаправить параметры на заданную функцию-член наблюдателей.
Если я попытаюсь передать адрес функции-члена, у которой несколько переопределений, он не может вывести правильную функцию-член на основе аргументов.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename Class>
struct observer_list
{
template<typename Ret, typename... Args, typename... UArgs>
void call(Ret (Class::*func)(Args...), UArgs&&... args)
{
for (auto obj : _observers)
{
(obj->*func)(std::forward<UArgs>(args)...);
}
}
std::vector<Class*> _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<foo> l;
foo f1, f2;
l._observers = { &f1, &f2 };
l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);
return 0;
}
Это невозможно скомпилировать с помощью template argument deduction/substitution failed
.
Обратите внимание, что у меня были Args...
и UArgs...
, потому что мне нужно иметь возможность передавать параметры, которые не обязательно являются одним и тем же типом типа типа сигнатуры функции, но могут быть конвертированы в указанный тип.
Я думал, что могу использовать вызов std::enable_if<std::is_convertible<Args, UArgs>>
для устранения неоднозначности, но я не верю, что могу сделать это с помощью пакета параметров вариационного шаблона?
Как я могу заставить вывод аргумента шаблона работать здесь?
Ответы
Ответ 1
Проблема здесь:
l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);
Для обеих строк компилятор не знает, к какому foo::func
вы обращаетесь. Следовательно, вы должны устранить проблему, предоставив информацию о типе, которая отсутствует (т.е. Тип foo:func
), посредством трансляций:
l.call(static_cast<void (foo::*)(const std::string&)>(&foo::func), "hello");
l.call(static_cast<void (foo::*)(const double )>(&foo::func), 0.5);
В качестве альтернативы вы можете предоставить аргументы шаблона, которые не может вывести компилятор, и определить тип func
:
l.call<void, const std::string&>(&foo::func, "hello");
l.call<void, double >(&foo::func, 0.5);
Обратите внимание, что вы должны использовать double
, а не const double
выше. Причина в том, что обычно double
и const double
являются двумя разными типами. Однако существует одна ситуация, когда double
и const double
рассматриваются так, как если бы они были одного типа: как аргументы функции. Например,
void bar(const double);
void bar(double);
не являются двумя разными перегрузками, но фактически являются одной и той же функцией.