消除作为模板参数传递的重载成员函数指针的歧义

2021-12-13 00:00:00 templates c++ c++11 typetraits

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

I am attempting to recreate the Observer pattern where I can perfectly forward parameters to a given member function of the observers.

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

If I attempt to pass the address of a member function which has multiple overrides, it cannot deduce the correct member function based on the arguments.

#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;
}

这无法编译,模板参数推导/替换失败.

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

Note that I had Args... and UArgs... because I need to be able to pass parameters which are not necessarily the same type asthe type of the function signature, but are convertible to said type.

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

I was thinking I could use a std::enable_if<std::is_convertible<Args, UArgs>> call to disambiguate, but I don't believe I can do this with a variadic template parameter pack?

我怎样才能让模板参数推导在这里起作用?

推荐答案

问题在这里:

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

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

For both lines, the compiler doesn't know which foo::func you are referring to. Hence, you have to disambiguate yourself by providing the type information that is missing (i.e., the type of foo:func) through casts:

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 的类型:

Alternatively, you can provide the template arguments that the compiler cannot deduce and that define the type of 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 被认为是相同的类型:作为函数参数.例如,

Notice that you have to use double and not const double above. The reason is that generally double and const double are two different types. However, there's one situation where double and const double are considered as if they were the same type: as function arguments. For instance,

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

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

are not two different overloads but are actually the same function.

相关文章