#c #function #polymorphism #subclass #std-function
#c #функция #полиморфизм #подкласс #std-функция
Вопрос:
[Обновление] Причина этого вопроса: существует множество существующих лямбд, определенных как [](const ChildType1amp; child)
, все в большом реестре. Мы хотим зарегистрировать новые лямбды, как [](const ChildType2amp; child)
в том же реестре. Если мы определяем оболочку функции с помощью Parent
, для многих существующих лямбд нам нужно изменить их на [](const Parentamp; someone)
, а внутри понизить с Parent
на ChildType1
.
Если у меня есть оболочка функции as std::function<void(const Parentamp;)>
, есть ли какой-либо способ разрешить ей принимать функцию с Parent
подклассом в качестве параметра, например, , [](const Childamp; child){...}
, где Child
является подклассом of Parent
.
Что-то ниже не компилируется. Онлайн-ссылка на IDE.
#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 Parentamp;)> Wrapper;
int main() {
Wrapper func=[](const Childamp; child){ // of course works if Child->Parent
child.say();
};
Child c;
func(c);
return 0;
}
Комментарии:
1. Нет, это невозможно, это логически нарушено, поскольку можно передать a
Parent
в функцию, которая ожидает aChild
.2. Нет, это неправильный тип отклонения. См. раздел Ковариация и контравариантность.
3. Есть ли причина, по которой вы не хотите это делать
Wrapper func=[](const Parentamp; child) { .. };
? Вы все равно можете вызвать его с помощью aChild
(и егоsay()
функция будет вызвана).4. Причина в том, что существует множество существующих лямбд, определенных как
[](const ChildType1amp; child)
, все в большом реестре. Мы хотим зарегистрировать новые лямбды, как[](const ChildType2amp; child)
в том же реестре. Если мы определяем оболочку функции с помощьюParent
, во многих существующих лямбда-выражениях нам нужно выполнить преобразование с понижением отParent
доChildType1
.5. @Robert Я не понимаю, зачем тебе нужно здесь опускать руки. У вас есть полиморфизм, поэтому вы можете просто использовать это.
Ответ №1:
Почему это запрещено?
Это не допускается языком, поскольку это может привести к несоответствиям.
Согласно вашему определению 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 Child1amp; child){ // if this is legitimate
child.tell(); // then this would be legitimate
};
И вы легко догадаетесь, что f(x)
и f(z)
не сработало бы, хотя тип f
должен это допускать.
Есть ли обходной путь?
Что вы можете сделать, но это что-то более рискованное, так это создать функцию-оболочку, которая принимает Parent
аргумент и преобразует его в a Child
. Но я бы не рекомендовал это делать, если только нет другого решения и только с особой осторожностью.
using Wrapper = std::function<void(const Parentamp;)>;
int main() {
Wrapper func=[](const Parentamp; parent){
auto child=dynamic_cast<const Child*>(amp;parent);
if (child)
child->say();
else std::cout<<"OUCH!!! I need a child"<<std::endl;
};
Parent x;
Child c;
func(c);
func(x);
}