向上投射函数指针安全吗
Is it safe to upcast a function pointer?
我有基类Object
和一个Event
class Object
{
//...
};
class Event
{
};
和一个函数指针的typedef
typedef void (Object::*PF) (Event*);
和一个不相关的类,它存储两个指针
class Dispatcher
{
public:
Dispatcher (Object* obj, PF pf) : _obj (obj), _pf (pf)
void call (Event* event)
{
_obj->*pf (event);
}
Object* _obj;
PF _pf;
};
然后我有一个具体的对象和一个具体事件
class ConcreteEvent : public Event
{
};
class ConcreteObject : public Object
{
public:
void handle (ConcreteEvent* event)
{
// Do something specific for ConcreteEvent
}
};
然后把它叫做
ConcreteObject* obj = new ConcreteObject();
Dispatcher dispatcher (obj, static_cast<PF>(&ConcreteObject::handle));
ConcreteEvent* event = new ConcreteEvent ();
dispatcher.call (event);
我保证调度程序将始终使用正确的事件进行调用,即当它封装的函数指针实际上采用SomeOtherConcreteEvent
时,我不会调用调度程序并将其传递给ConcreteEvent
问题是:这能保证奏效吗?在linux和mingw上,Is在gcc 4.7中都可以正常工作。
来自C++11标准,第4.11.2节:
类型为"指向cv T类型的B的成员的指针"的prvalue,其中B是类类型,可以转换为类型为"指向cv T类型的D的成员的指针"的prvalue,其中D是B的派生类(第10条)。如果B是D的不可访问(第11条)、不明确(10.2)或虚拟(10.1)基类,或虚拟的基类D的基类,一个需要这种转换的程序是不正确的。转换的结果指指向与转换发生前指向成员的指针相同的成员,但它指的是基类成员,就好像它是派生类的成员一样。
所以是的,这应该是安全的。
编辑:因此,根据C++11 5.2.9.12:,如果您实际上是指向下广播:,那也是合法的
类型为"pointer to member of D of type cv1 T"的prvalue可以转换为类型为"pointer to类型为cv2T的"B的成员",其中B是D的基类(第10条),如果从存在"指向类型T的B的成员的指针"到"指向类型T的D的成员的指示器"(4.11),并且cv2是相同的cv资格等于或大于cv1。69
我认为不安全,原因有两个。
首先,指向成员函数的指针只能安全地向下传播(因为Derived
类必须从基类继承了函数,而反过来则不然)。
class Base {
public:
void foo();
}; // class Base
class Derived: public Base {
public:
void bar();
};
using PBase = void (Base::*)();
using PDerived = void (Derived::*)();
int main() {
PBase pb = &Base::foo;
PDerived pd = &Derived::bar;
pb = pd; // error: cannot convert 'PDerived {aka void (Derived::*)()}'
// to 'PBase {aka void (Base::*)()}' in assignment
pd = pb;
}
(如图所示)
第二个问题是,不能像那样更改参数的类型。为了说明这个问题,使用ConcreteObject: public virtual Object
,您会发现它并不像您希望的那样工作。
现在,这并不意味着你想做的事情是不可能的,只是它需要多一点。
理想情况下,不使用成员函数,只需修复签名,使其同时接受Object
和Event
,然后在必要时让它处理手动强制转换:
using Function = std::function<void(Object*,Event*)>;
void handle(Object* o, Event* e) {
ConcreteObject* co = dynamic_cast<ConcreteObject*>(o);
ConcreteEvent* ce = dynamic_cast<ConcreteEvent*>(e);
if (co and ce) { co->handle(ce); }
}
或者任何你觉得舒服的造型/检查。
注意:使用std::function
是为了与lambdas/functors兼容
- 在函数结束后使用指向变量的指针是否安全?
- char p[0]表示自动分配的缓冲区还是安全指针
- 通过std::shared_ptr使用Rcpp和RcppParallel的线程安全函数指针
- 用非零值初始化void指针的正确(或最安全)方法
- 初始化期间针对安全检查的指针的恒定正确性
- 这C++指针使用线程安全吗?
- 带有此指针的内存安全吗?
- 是否可以访问非线程安全容器内指针指向的值(线程安全映射中的条目)?
- 存储指向 std::string 数据的指针是否安全?
- 为什么引用比指针更安全?
- 为什么在构造函数中将字符串分配给指针是安全的?
- 是否访问指针元组和互斥锁线程安全
- 将数据成员的指针传递给基类构造函数是否安全?
- 将C 中的每个指针删除作为阵列的指针安全吗?
- 重新分配指针阵列的一部分的安全方法
- C 中的结构偏移和指针安全
- 存储STL列表迭代器的指针安全吗?
- 对基元/类使用模板和指向基元/类的指针安全吗?
- 向上投射函数指针安全吗
- 用于空指针安全访问的C/ c++宏