Я делаю какую-то оболочку, которая выглядит следующим образом:
#include <iostream>
template<class T, class Value>
void Apply(void (T::*cb)(Value), T* obj, Value v)
{
(obj->*cb)(v);
}
class Foo
{
public:
void MyFunc(const int& i)
{
std::cout << i << std::endl;
}
const int& GetValue()
{
return i_;
}
private:
int i_ = 14;
};
int main()
{
Foo f;
Apply(&Foo::MyFunc, &f, f.GetValue());
}
И я получаю эту ошибку:
Apply
: не найдено подходящей перегруженной функции. void Apply(void (__thiscall T::* )(Value),T *,Value)
: параметр шаблона Value
неоднозначно, может быть int
или же const int &
, void Apply(void (__thiscall T::* )(Value),T *,Value)
: не удалось вывести аргумент шаблона для Value
от const int
, Итак, я понял, что это происходит из вывода параметров шаблона, однако я не понимаю, как. Почему бы Value
не оценить const int&
оба раза?
В настоящее время параметр шаблона Value
выводится в двух разных местах при вызове Apply
: от указателя на аргумент функции-члена и от последнего аргумента. От &Foo::MyFunc
, Value
выводится как int const&
, От f.GetValue()
, Value
выводится как int
, Это связано с тем, что cv-квалификаторы верхнего уровня и ссылки отбрасываются для вывода шаблона. Поскольку эти два вычета для параметра Value
отличаются, вычет не удается — что удаляет Apply()
из набора перегрузки, и в результате мы не имеем жизнеспособной перегрузки.
Проблема в том, что Value
выводится в двух разных местах, так что давайте просто не допустить этого. Один из способов заключается в том, чтобы обернуть одно из применений в не выводимый контекст:
template <class T> struct non_deduced { using type = T; };
template <class T> using non_deduced_t = typename non_deduced<T>::type;
template<class T, class Value>
void Apply(void (T::*cb)(Value), T* obj, non_deduced_t<Value> v)
{
(obj->*cb)(v);
}
Последний аргумент, v
, имеет тип non_deduced_t<Value>
который, как следует из названия, является не выводимым контекстом. Таким образом, в процессе вывода шаблона, Value
выводится как int const&
от указателя на функцию-член (как раньше), и теперь мы просто подключаем это к типу для v
,
Кроме того, вы можете выбрать, чтобы вывести cb
в качестве собственного параметра шаблона. В какой момент Apply()
просто сводится к std::invoke()
,
Выражение f.GetValue()
является lvalue типа const int
, Когда это передается по значению, вычет аргумента шаблона выводит тип int
, В общем, выводя Value
от Value v
будут никогда создать ссылку или тип с квалификацией высшего уровня.
Возможно, вы захотите иметь два отдельных параметра шаблона вместо Value
(один для аргумента типа функции, один для фактического типа аргумента) и используйте SFINAE для отключения Apply
когда cb
не вызывается с v
(или же static_assert
за серьезную ошибку).