C++11 std::function 不接受多态参数

C++11 std::function doesn't accept polymorphic parameters

在 g++ 4.9.2 下无法编译低于 C++11 的代码(应该符合我的第一印象):

class A{};
class B : public A{};

void func(shared_ptr<B>){}
TEST(testFunc, testFunc_conv){
    std::function<void(shared_ptr<A>)> afunc;
    afunc = func;
}

错误消息表明它不接受从 shared_ptr<B>shared_ptr<A> 的转换,尽管 B 可以转换为 A

为什么这不起作用?是否可以解决此限制?

编辑 我仔细考虑了其中的含义并理解了原因 - A 确实不能转换为 B,所以为了类型安全不允许这样做。

这段代码的背景是实现一些带有可变参数的通用接口,这样世界的其他地方就可以注册一个可调用的,它采用空基类型的派生。稍后将调用可调用对象(一种延迟调用):

//action would be stored for deferred call
//  when action is actually called, it will use the real type
template<class ObjType>
registerInterface(function<void(Base*)> action, ObjType* obj){
    function<void()> callable = [=]{ action(obj);}
    //callable action would be stored for deferred call
}

void interfaceImpl(Derived* d){
    d->realOperation();
}

//do registration - need to do the conversion each time for each interface implementation!
Derived d;
registerInterface(interfaceImpl, &d); 

每个 interfaceImpl 都声明为采用基类型并粗暴地进行向下转换会很烦人。

我的解决方案是从接口中删除函数并为 interfaceImpl 设置隐式可调用模板参数以指定。如果有更好的解决方案,不胜感激。

如果你想要多态性,声明应该包括 parent class 这样你就可以将它的 children 传递给它。

您在定义中使用了特定的 child,然后尝试将该函数与基本函数一起使用。显然这是行不通的。

继承自 B 或更改声明以使用 class A.


注:

我很确定您可以用 C++ 实现您想要实现的目标。但是,您应该问自己的问题是:"Just because I can, should I really do it?" 例如,仅仅因为您可以滥用指针来检索 class 的私有成员并不意味着您应该这样做,并且创建访问器几乎永远是更好的选择。

请记住,代码被阅读和审查的频率远高于编写的频率。我更愿意看到一个简单的代码,而不是阅读一段代码,包括语言的特殊结构只是为了让它工作。

你写"those 2 types are convertible"。他们不是。

由于 B 是 A 的子类型,因此可以假定 B 包含其他成员。因此不允许做你正在尝试的事情。你是说 func 需要一个指向 B 的指针来操作。

然后你创建一个函数指针,明确声明你将发送类型 A 的东西。这是行不通的,因为正如所述 B 是派生的 class 可能包含比 A 更多的成员,func 上的成员可能依赖(因为它明确地将其指定为参数类型)。

不过,反之亦然。您可以在需要类型 A 的地方传递类型 B,因为 A 是基本类型,这将确保 B 具有相同的成员,因此传递它是安全的(因为它是类型 A)。

shared_ptr<B> 可转换为 shared_ptr<A>。责任到此为止。以下类型不可转换:

shared_ptr<B>* to shared_ptr<A>*
void(*)(shared_ptr<B>) to void(*)(shared_ptr<A>)
function<void(shared_ptr<B>)> to function<void(shared_ptr<A>)>

这是有充分理由的。下一个片段会发生什么?

func (new A); // should not compile
afunc = func; // imagine this is allowed
afunc(new A); // now what?

上面但方向相反的一些转换确实有意义,但由于一些历史和其他原因,C++ 不允许它们。

幸运的是你不需要这些。你可以做到

template<class ObjType>

registerInterface(函数动作, ObjType* obj)

或者,更好

registerInterface(std::function<void()> func) ... 

然后调用

register_interface(std::bind(funcA,objA));  
register_interface(std::bind(funcB,objB));