我可以在基类模板方法中自动推断子类型吗?

时间:2015-11-30 15:25:27

标签: c++ templates c++11 c++14

首先,让我提供一些背景信息。我正在为我的游戏创建一个小游戏框架,我有游戏系统和事件系统。我尝试使用模板等语言功能来避免用户代码中的样板。

有Component类,从中派生出其他游戏元素,即Camera,Sprite,Button。

using EventType = unsigned int;
using EventCallback = std::function<void(Event)>;

class Component {
public:
    // This is public version for general functor objects,
    // particularly used with lambda
    void addHandler(EventType type, const EventCallback &callback);

protected:
    // Method for child classes. Creates event handler from method of the class.
    //
    // This is template method, because std::bind is used for functor creation,
    // which requires class type at compile time.
    template <class T>
    void addHandler(EventType type, void (T::*method)(Event event)) { /*...*/ }
private:
    // ...
};

每个组件都会侦听特定的事件集,因此不必为每种可能的事件类型实现处理程序。此外,组件用户应该能够添加自定义事件侦听器,而无需为每个游戏元素创建新类,例如:

class Button : public Component {
public:
    Button() {
        addHandler(kOnTouch, &Button::onTouch);
    }
    // ...
};

Button ok, cancel;
ok.addHandler(kOnClick, [](Event) {
    // ...
});

cancel.addHandler(kOnClick, [](Event) {
    // ...
});

// Add another handler somewhere else in the code
cancel.addHandler(kOnClick, someCallback);

所以,我想做的是后期绑定,对成员函数进行编译时检查。我想确保传递给addHandler()的方法指针属于调用addHandler()的子类。我可以在template argument deduction的帮助下获取addHandler()中方法所有者的类型。但我没有找到推断子类类型的方法。这里我试图用decltype(*this)和类型特征来完成这个:

template <class T>
void addHandler(EventType type, void (T::*method)(Event event)) {
    /***** This check is INCORRECT *****/
    // Check that T is same as child class
    using ChildClass = std::remove_reference<decltype(*this)>::type;
    static_assert(std::is_same<T, ChildClass>::value,
                  "Event handler method must belong to caller class");

    using namespace std::placeholders;

    EventHandler::Callback callback =
            std::bind(method, static_cast<T *>(this), _1);
    addHandler(EventHandler(type, callback));
}

这里我们需要子类类型来比较T和。似乎ChildClass被分配给基础Component类而不是子类。有没有办法自动推导子类类型,只在方法版本addHandler()内部更改?重要的是只模拟这个重载的addHandler()而不是整个Component类来最小化生成的代码并能够使用多态。所以它是更通用的addHandler()的小包装器,采用std :: function。

目前,我只能检查T是否为分量:

static_assert(std::is_base_of<Component, T>::value,
              "Event handler method must belong to caller class");

1 个答案:

答案 0 :(得分:1)

  

似乎ChildClass被分配给基础Component类而不是子类。

这里没有任何“分配”。滥用术语令人困惑。

基类的成员函数中this的类型(当然)是基类。

要知道调用者的类型,您需要调用者的this指针,而不是基类成员函数中的this指针。您可以通过要求调用者将其作为参数传递来获取调用者的this指针:

template <class T, class U>
  void addHandler(EventType type, void (T::*method)(Event event), U* that)

然后你甚至不需要静态断言,你可以将that绑定到method

N.B。如果你保持静态断言,你可能想要使用std::is_base_of而不是std::is_same

或者你可以摆脱addHandler重载并只需要派生类型来进行绑定:

class Button : public Component {
public:
  Button() {
    addHandler(kOnTouch, [this](Event e) { onTouch(e); });
  }
  // ...
};