模板参数不明确:无法推断模板参数

时间:2016-11-08 21:44:50

标签: c++ templates ambiguous template-deduction ambiguous-call

我正在做一些看起来像这样的包装器:

#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不明确,可以是intconst int &
  • void Apply(void (__thiscall T::* )(Value),T *,Value):无法从Value推断const int的模板参数。

所以我得到它来自模板参数演绎,但我不明白如何。为什么Value 两次评估为const int&

2 个答案:

答案 0 :(得分:13)

为什么失败

目前,模板参数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()

答案 1 :(得分:3)

表达式f.GetValue()const int类型的左值。当按值传递时,模板参数推导会推导出类型int。一般情况下,从Value推断Value v 永远不会生成具有顶级cv资格的参考或类型。

您可能希望有两个单独的模板参数来代替Value(一个用于函数类型&#39; s参数,一个用于实际参数类型)并使用SFINAE来禁用{{ 1}}当Apply无法使用cb(或v进行硬错误调用)时。{/ p>