C++ std::function 使用 sub class 参数获取函数
C++ std::function to take functions with sub class parameter
[更新]这个问题的原因:
有许多现有的 lambda 定义为 [](const ChildType1& child)
,都在一个大注册表中。我们想在同一个注册表中注册新的 lambda,例如 [](const ChildType2& child)
。如果我们使用 Parent
定义函数包装器,对于许多现有的 lambda,我们需要将它们更改为 [](const Parent& someone)
,并在内部从 Parent
向下转换为 ChildType1
.
如果我有一个 std::function<void(const Parent&)>
的函数包装器,有什么方法可以让它接受一个带有 Parent
子类的函数作为参数,例如 [](const Child& child){...}
,其中 Child
是 Parent
.
的子类
以下内容无法编译。在线IDElink.
#include <iostream>
#include <functional>
class Parent {
public:
virtual void say() const {
std::cout<<"I am parent"<<"\n";
}
};
class Child: public Parent {
public:
void say() const {
std::cout<<"I am child"<<"\n";
}
};
typedef std::function<void(const Parent&)> Wrapper;
int main() {
Wrapper func=[](const Child& child){ // of course works if Child->Parent
child.say();
};
Child c;
func(c);
return 0;
}
为什么不允许这样做?
语言不允许这样做,因为它可能会导致不一致。
根据您对 Wrapper
的定义,以下代码应该是合法的:
Wrapper f;
Parent x;
... // Initialize f with a legitimate function dealing Parent
f(x);
现在想象两个类:
class Child1: public Parent {
public:
void say() const {
std::cout<<"I am child1"<<"\n";
}
virtual void tell() const {
std::cout<<"This is specific to child1"<<"\n";
}
};
class Child2: public Parent {
public:
void say() const {
std::cout<<"I am child2"<<"\n";
}
};
以下代码也有效,因为 Child1
和 Child2
派生自 Parent
:
Child1 y;
Child2 z;
f(y);
f(z);
如果你被允许为你的包装器分配一个带有子参数而不是父参数的函数,你也可以这样做:
Wrapper f=[](const Child1& child){ // if this is legitimate
child.tell(); // then this would be legitimate
};
你很容易猜到 f(x)
和 f(z)
不会工作,尽管 f
的类型应该允许它。
有解决办法吗?
您可以做的是创建一个包装函数,该函数采用 Parent
参数并向下转换为 Child
。但我不推荐它,除非没有其他解决方案并且只有格外小心。
using Wrapper = std::function<void(const Parent&)>;
int main() {
Wrapper func=[](const Parent& parent){
auto child=dynamic_cast<const Child*>(&parent);
if (child)
child->say();
else std::cout<<"OUCH!!! I need a child"<<std::endl;
};
Parent x;
Child c;
func(c);
func(x);
}
[更新]这个问题的原因:
有许多现有的 lambda 定义为 [](const ChildType1& child)
,都在一个大注册表中。我们想在同一个注册表中注册新的 lambda,例如 [](const ChildType2& child)
。如果我们使用 Parent
定义函数包装器,对于许多现有的 lambda,我们需要将它们更改为 [](const Parent& someone)
,并在内部从 Parent
向下转换为 ChildType1
.
如果我有一个 std::function<void(const Parent&)>
的函数包装器,有什么方法可以让它接受一个带有 Parent
子类的函数作为参数,例如 [](const Child& child){...}
,其中 Child
是 Parent
.
以下内容无法编译。在线IDElink.
#include <iostream>
#include <functional>
class Parent {
public:
virtual void say() const {
std::cout<<"I am parent"<<"\n";
}
};
class Child: public Parent {
public:
void say() const {
std::cout<<"I am child"<<"\n";
}
};
typedef std::function<void(const Parent&)> Wrapper;
int main() {
Wrapper func=[](const Child& child){ // of course works if Child->Parent
child.say();
};
Child c;
func(c);
return 0;
}
为什么不允许这样做?
语言不允许这样做,因为它可能会导致不一致。
根据您对 Wrapper
的定义,以下代码应该是合法的:
Wrapper f;
Parent x;
... // Initialize f with a legitimate function dealing Parent
f(x);
现在想象两个类:
class Child1: public Parent {
public:
void say() const {
std::cout<<"I am child1"<<"\n";
}
virtual void tell() const {
std::cout<<"This is specific to child1"<<"\n";
}
};
class Child2: public Parent {
public:
void say() const {
std::cout<<"I am child2"<<"\n";
}
};
以下代码也有效,因为 Child1
和 Child2
派生自 Parent
:
Child1 y;
Child2 z;
f(y);
f(z);
如果你被允许为你的包装器分配一个带有子参数而不是父参数的函数,你也可以这样做:
Wrapper f=[](const Child1& child){ // if this is legitimate
child.tell(); // then this would be legitimate
};
你很容易猜到 f(x)
和 f(z)
不会工作,尽管 f
的类型应该允许它。
有解决办法吗?
您可以做的是创建一个包装函数,该函数采用 Parent
参数并向下转换为 Child
。但我不推荐它,除非没有其他解决方案并且只有格外小心。
using Wrapper = std::function<void(const Parent&)>;
int main() {
Wrapper func=[](const Parent& parent){
auto child=dynamic_cast<const Child*>(&parent);
if (child)
child->say();
else std::cout<<"OUCH!!! I need a child"<<std::endl;
};
Parent x;
Child c;
func(c);
func(x);
}