【问题标题】:Disambiguate overloaded member function pointer being passed as template parameter消除作为模板参数传递的重载成员函数指针的歧义
【发布时间】:2013-07-26 07:38:22
【问题描述】:

我正在尝试重新创建 观察者模式,我可以在其中完美地将参数转发给观察者的给定成员函数。

如果我尝试传递具有多个覆盖成员函数的地址,它无法根据参数推断出正确的成员函数。

#include <iostream>
#include <vector>
#include <algorithm>

template<typename Class>
struct observer_list
{
    template<typename Ret, typename... Args, typename... UArgs>
    void call(Ret (Class::*func)(Args...), UArgs&&... args)
    {
        for (auto obj : _observers)
        {
            (obj->*func)(std::forward<UArgs>(args)...);
        }
    }
    std::vector<Class*> _observers;
};

struct foo
{
    void func(const std::string& s)
    {
        std::cout << this << ": " << s << std::endl;
    }
    void func(const double d)
    {
        std::cout << this << ": " << d << std::endl;
    }
};

int main()
{
    observer_list<foo> l;
    foo f1, f2;
    l._observers = { &f1, &f2 };

    l.call(&foo::func, "hello");
    l.call(&foo::func, 0.5);

    return 0;
}

使用template argument deduction/substitution failed 编译失败。

请注意,我有 Args...UArgs...,因为我需要能够传递的参数不一定与函数签名的类型相同,但可以转换为所述类型。

我在想我可以使用 std::enable_if&lt;std::is_convertible&lt;Args, UArgs&gt;&gt; 调用来消除歧义,但我不相信我可以使用可变参数模板参数包来做到这一点?

如何让模板参数推导在这里起作用?

【问题讨论】:

  • call 怎么改都没关系,问题就在这上面。另外,非常重要:不要在 call 中使用 std::forward 作为参数,因为它们可能会以这种方式多次转发,并且只有一个观察者会收到未移动的右值参数。

标签: c++ templates c++11 typetraits


【解决方案1】:

问题就在这里:

l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);

对于这两行,编译器不知道您指的是哪个foo::func。因此,您必须通过强制转换提供缺失的类型信息(即foo:func 的类型)来消除歧义:

l.call(static_cast<void (foo::*)(const std::string&)>(&foo::func), "hello");
l.call(static_cast<void (foo::*)(const double      )>(&foo::func), 0.5);

或者,您可以提供编译器无法推断的模板参数并定义func 的类型:

l.call<void, const std::string&>(&foo::func, "hello");
l.call<void, double            >(&foo::func, 0.5);

请注意,您必须在上面使用double 而不是const double。原因是一般doubleconst double是两种不同的类型。但是,在一种情况下,doubleconst double 被认为是同一类型:作为函数参数。例如,

void bar(const double);
void bar(double);

不是两个不同的重载,而是实际上是同一个函数。

【讨论】:

  • 我已经在签名 std::vector::iterator std::vector::at ( std::vector::iterator, const std::vector::value_type& ),它不起作用。我原以为这是由于 constness 造成的,但是在第二个参数中没有 constness 的相同重载。编译器(msvc 13)抱怨它无法从重载函数转换为非重载签名。
  • @user2813810 std::vector&lt;int&gt;::at 只有两个重载,即const_reference at(size_type n) constreference at(size_type n)(例如,参见here)。他们都没有接受或返回迭代器。所以,我不确定我是否关注你。你为什么不发布一个新问题(带有一个简短的自包含示例)并交叉引用这篇文章说它没有解决你的问题?可能是我或其他人可以提供帮助。
  • 是的,签名就是问题所在。我正在通过宏同时使用容器中的每种方法,非常困倦并且感到困惑。原来宏是不正确的,它结合了两个“方法检测器”,编译器错误实际上使它看起来像是 std::vector::at 的签名。宏是邪恶的另一种情况。从那以后发生了很大的变化,我什至无法复制它。如果我在发现问题时没有忘记这篇文章,我会回来删除。你知道当你最终发现这个错误时你是如何获得隧道视野的吗?
猜你喜欢
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多