Идеальная пересылка параметров шаблона переменной в функцию-член

Я пытаюсь реализовать шаблон наблюдателя с примером использования следующим образом:

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();
}

Теперь моя текущая (частичная) реализация ObserverList является:

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)...);
         }
    }
}

Это работает как ожидалось с функциями SomethingHappened а также BoundsChanged который передает все свои параметры по ссылке, а не по значению, но с TextChanged один из его параметров (const char* text) передается по значению, а не по ссылке. Когда я изменил это на const char*& text он компилируется, но не компилируется с 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

Похоже, что компилятору не нравятся параметры, передаваемые по значению, есть ли способ сделать его похожим на них?

1 ответ

Решение

Вы можете объявить func иметь простой тип Fт.е.

template <typename F, typename ...TArgs> 
void Fire(F func, TArgs&&... args);
Другие вопросы по тегам