模板参数不明确:无法推断模板参数
Template parameter is ambiguous: could not deduce template argument
我正在做类似这样的包装:
#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)
:无法从 const int
推断出 Value
的模板参数。
所以我知道它来自模板参数推导,但是我不明白如何。为什么 Value
不会 两次都计算为 const int&
?
表达式 f.GetValue()
是类型 const int
的左值。当它按值传递时,模板参数推导推导类型 int
。通常,从 Value v
推导 Value
将 永远不会 产生引用或具有顶级 cv 限定的类型。
您可能希望用两个单独的模板参数代替 Value
(一个用于函数类型的参数,一个用于实际参数类型)并使用 SFINAE 禁用 Apply
cb
不能用 v
调用(或 static_assert
硬错误)。
为什么会失败
目前,模板参数 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()
。
我正在做类似这样的包装:
#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)
:无法从const int
推断出Value
的模板参数。
所以我知道它来自模板参数推导,但是我不明白如何。为什么 Value
不会 两次都计算为 const int&
?
表达式 f.GetValue()
是类型 const int
的左值。当它按值传递时,模板参数推导推导类型 int
。通常,从 Value v
推导 Value
将 永远不会 产生引用或具有顶级 cv 限定的类型。
您可能希望用两个单独的模板参数代替 Value
(一个用于函数类型的参数,一个用于实际参数类型)并使用 SFINAE 禁用 Apply
cb
不能用 v
调用(或 static_assert
硬错误)。
为什么会失败
目前,模板参数 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()
。