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