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

时间:2015-11-18 07:02:46

标签: c++ function c++11 covariance

在C ++ 11下面的代码将无法在g ++ 4.9.2下编译(应该是我的第一印象):

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,因此为了类型安全而不允许这样做。

此代码的背景是实现一些带有可变参数的通用接口,因此世界的其他部分可以注册一个可调用的,它可以获取空基类型的衍生物。稍后将调用callable(延迟调用的种类):

//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设置隐式可调用模板参数以指定。感谢是否有更好的解决方案。

3 个答案:

答案 0 :(得分:4)

如果你想要多态,声明应该包含类,这样你就可以将它的子句传递给它。

您在定义中使用特定子项,然后尝试将该函数与基本项一起使用。显然这不起作用。

B继承或更改声明以使用课程A

注意:

我很确定你可以做你想用C ++实现的目标。但是,你应该问自己的问题是:“只因为我可以,我真的应该这样做吗?”例如,仅仅因为你可以滥用指针来检索一个类的私有成员并不意味着你应该这样做,并且创建一个访问器几乎总是更好的选择。

请记住,代码经常被阅读和审核而不是书面形式。我宁愿看到一个简单的代码,而不是阅读一些代码,包括该语言的特殊结构,以使其有效。

答案 1 :(得分:1)

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));

答案 2 :(得分:0)

你写“那两种类型是可转换的”。他们不是。

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

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

相反,另一种方式是多态的。您可以传递类型B,其中A类型是预期的,因为A是基类型,这将确保B具有相同的成员,因此传递它是安全的(因为它是类型A)。