Rikus Honey
Rikus Honey

Reputation: 558

Perfect forwarding variadic template parameters to member function

I am trying to implement the observer pattern with example usage as follows:

class Widget
{
    class WidgetObserver
    {
        virtual void SomethingHappened(Widget&) { };
        virtual void TextChanged(Widget&, const char*) { };
        virtual void BoundsChanged(Widget&, const Rect&) { };
    }

    void AddObserver(WidgetObserver& observer)
    {
        observers.Add(observer);
    }

    virtual void OnSomthingHappened()
    {
        observers.Fire(&WidgetObserver::SomethingHappened, *this);
    }

    virtual void OnTextChanged(const char* text)
    {
        observers.Fire(&WidgetObserver::TextChanged, *this, text);
    }

    // Same for OnBoundsChanged(const Rect&)

    ObserverList<Widget> observers;
}

class AnotherWidget : WidgetObserver
{
    virtual void SomethingHappened(Widget& widget) override
    {
         std::cout << "Something happened with " widget.name() << std::endl;
    }

    // Implementations for TextChanged and BoundsChanged ...
}

void main()
{
    Widget w;
    AnotherWidget aw;
    w.AddObserver(aw);
    w.OnSomethingHappened();
}

Now my current (partial) implementation of ObserverList is:

template <typename TObserver>
class ObserverList
{
    std::vector<TObserver*> observer_list;

    void AddObserver(TObserver& observer) { observer_list.push_back(&observer); }

    template <typename ...TArgs>
    void Fire(void(TObserver::*func)(TArgs...), TArgs&& args)
    {
         for (TObserver* ob : observer_list)
         {
             (ob->*func)(std::forward<TArgs>(args)...);
         }
    }
}

This works as expected with the functions SomethingHappened and BoundsChanged which both passes all its parameters by reference rather than by value, but with TextChanged one of its parameters (const char* text) is passed by value rather than reference. When I changed this to const char*& text it compiles but as is it doesn't compile with MSVC++ 14.1 (Visual Studio 2017)

error C2672: 'ObserverList<Widget::WidgetObserver>::Fire': no matching overloaded function found
error C2782: 'void ObserverList<Widget::WidgetObserver>::Fire(void (__thiscall Widget::WidgetObserver::* )(TArgs...),TArgs &&...)': template parameter 'TArgs' is ambiguous

Seems like the compiler doesn't like parameters passed by value, is there any way to make it like them?

Upvotes: 0

Views: 624

Answers (1)

xskxzr
xskxzr

Reputation: 13040

You can declare func to have a simple type F, i.e.

template <typename F, typename ...TArgs> 
void Fire(F func, TArgs&&... args);

Upvotes: 1

Related Questions