【问题标题】:Template parameter is ambiguous: could not deduce template argument模板参数不明确:无法推断模板参数
【发布时间】:2017-03-22 16:09:05
【问题描述】:

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

#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 &amp;。
  • void Apply(void (__thiscall T::* )(Value),T *,Value):无法从 const int 推断出 Value 的模板参数。

所以我知道它来自模板参数推导,但是我不明白如何。为什么Value不会两次都评估为const int&amp;?

【问题讨论】:

  • 这个例子是对问题的重现,但绝不是我正在编写的代码per-se。

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


【解决方案1】:

为什么会失败

目前,在对Apply 的调用中,模板参数Value 在两个不同的位置推导出来:从指向成员函数参数的指针和从最后一个参数。从&amp;Foo::MyFunc,推导出Value为int const&amp;。从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&lt;Value&gt; 类型,顾名思义,它是一个非推导上下文。所以在模板推导过程中,Value 从指向成员函数的指针中推导出为int const&amp;(如前所述),现在我们只需将其插入到v 的类型中。

或者,您可以选择将cb 推断为它自己的模板参数。此时Apply() 只是减少到std::invoke()。

【讨论】:

  • 感谢您的想法,我实际上设法使用std::decay 做到了。
  • @AlexandreBourlon decay 在这里是一个糟糕的选择 - 如果 Value 是一个引用类型,那么您现在将把参数的副本传递给 cb,而不是你想要什么。
  • 您的解决方案不也是这样吗?那就是我一开始的想法......现在我正在使用std::decay_t&lt;ValueType&gt; const&amp;,所以如果他有类型松散引用和cv-qualifier,那么我在参数上强制使用const&。我知道它不直接等同于在调用站点传递的类型,但对于我的用例来说似乎已经足够好了。不过,我仍然愿意接受更好的建议。
  • @AlexandreBourlon 不,不是。考虑如果Value 是非常量左值引用会发生什么。您将通过 const 左值引用来获取您的论点并尝试通过它。
  • 所以我对 non-deduced context 进行了更多研究,发现了另一个 SO 问题:stackoverflow.com/questions/25245453/…。在这种情况下,您如何看待std::common_type?
【解决方案2】:

表达式f.GetValue() 是const int 类型的左值。当这是按值传递时,模板参数推导推导出类型int。一般来说,从 Value v 推导出 Value 将永远不会生成具有顶级 cv 限定的引用或类型。

您可能希望有两个单独的模板参数来代替 Value(一个用于函数类型的参数,一个用于实际参数类型)并在没有 cb 时使用 SFINAE 禁用 Apply可使用v 调用(或static_assert 表示硬错误)。

【讨论】:

    猜你喜欢
    • 2013-02-18
    • 1970-01-01
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    相关资源
    最近更新 更多