[Update] Reason for this question:
There are many existing lambdas defined as [](const ChildType1& child)
, all in a big registry. We want to register new lambdas like [](const ChildType2& child)
in the same registry. If we define the function wrapper using Parent
, for the many existing lambdas we need to change them to [](const Parent& someone)
, and inside downcast from Parent
to ChildType1
.
If I have a function wrapper as std::function<void(const Parent&)>
, is there any way to allow it take a function with Parent
subclass as parameter, e.g., [](const Child& child){...}
, where Child
is a subclass of Parent
.
Something below does not compile. Online IDE link.
#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;
}
CodePudding user response:
Why isn't this allowed ?
This is not allowed by the language because it might lead to inconsistencies.
With your definition of Wrapper
, the following code should be legitimate:
Wrapper f;
Parent x;
... // Initialize f with a legitimate function dealing Parent
f(x);
Now imagine two classes:
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";
}
};
The following code would also be valid, since Child1
and Child2
derive from Parent
:
Child1 y;
Child2 z;
f(y);
f(z);
If you were allowed to assign a function with a child argument instead of a parent argument for your wrapper, you could as well do something like:
Wrapper f=[](const Child1& child){ // if this is legitimate
child.tell(); // then this would be legitimate
};
And you'll easily guess that f(x)
and f(z)
would not work although the type of f
should allow it.
Is there a work-around?
What you can do, but this is something more risky, is to make a wrapper function that takes a Parent
argmument and down-casts is to a Child
. But I'd not recommend it unless there's no other solution and only with extra-care.
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);
}