首先,让我提供一些背景信息。我正在为我的游戏创建一个小游戏框架,我有游戏系统和事件系统。我尝试使用模板等语言功能来避免用户代码中的样板。
有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");
答案 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); });
}
// ...
};