我可以在基类模板方法中自动推导子类型吗
Can I automatically deduce child type in base class template method?
首先,让我提供一个位上下文。我正在为我的游戏创建一个小游戏框架,其中我有游戏系统和事件系统。我尝试使用诸如模板之类的语言特性来避免用户代码中的样板文件。
有一个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的子类。在模板参数推导的帮助下,我可以在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::函数。
目前,我只能检查T是否为组件:
static_assert(std::is_base_of<Component, T>::value,
"Event handler method must belong to caller class");
似乎ChildClass被分配给了基类Component,而不是子类。
此处未"分配"任何内容。滥用术语令人困惑。
基类的成员函数中this
的类型(当然)是基类。
要知道调用者的类型,需要调用者的this
指针,而不是基类成员函数内的this
指针。您可以通过要求调用者将其作为参数传递来获得调用者的this
指针:
template <class T, class U>
void addHandler(EventType type, void (T::*method)(Event event), U* that)
那么您甚至不需要静态断言,只需将that
绑定到method
即可
注意:如果保留静态断言,则可能希望使用std::is_base_of
而不是std::is_same
。
或者,您可以去掉addHandler
重载,只需要派生类型来进行绑定:
class Button : public Component {
public:
Button() {
addHandler(kOnTouch, [this](Event e) { onTouch(e); });
}
// ...
};
- 是否可以初始化不可复制类型的成员变量(或基类)
- 当基类是依赖类型时,这是一个缺陷吗
- 如何允许模板参数中的类类型,仅当它有两个基类时
- 如何使用 std::make_shared 创建基类类型的智能指针?
- 当目标指针不是基类的类型时,为什么允许dynamic_cast为多态类生成 null 指针?
- 为什么允许dynamic_cast到非唯一的基类类型?
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 我们可以在不知道其真实类型的情况下将基类指针转换为派生类指针吗?
- 从基类指针派生派生类的模板类型
- 基类类型向量中的派生结构
- 从基类的唯一指针中声明派生类的类型
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- 如何隐藏模板基类的参数类型
- 即使基类和派生类只使用基元数据类型,我是否需要定义虚拟析构函数
- 添加字符串类型的类成员会导致调用基类函数而不是子函数
- C++ 模板:重载时找不到基类类型参数方法
- 如何从包含基类指针的容器中调用派生类函数(基于其类型)?
- 为什么嵌套类型的基类不需要"typename"?
- 将空基类优化对象强制转换为另一种类型是否会破坏严格的别名?
- 从具有泛型返回类型的 crtp 基类调用派生类中的函数