Параметр шаблона неоднозначен: не удалось вывести аргумент шаблона

Я делаю какую-то оболочку, которая выглядит следующим образом:

#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& оба раза?

9

Решение

Почему это не удается

В настоящее время параметр шаблона 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(),

13

Другие решения

Выражение f.GetValue() является lvalue типа const int, Когда это передается по значению, вычет аргумента шаблона выводит тип int, В общем, выводя Value от Value v будут никогда создать ссылку или тип с квалификацией высшего уровня.

Возможно, вы захотите иметь два отдельных параметра шаблона вместо Value (один для аргумента типа функции, один для фактического типа аргумента) и используйте SFINAE для отключения Apply когда cb не вызывается с v (или же static_assert за серьезную ошибку).

3

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector