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