C std :: функция для выполнения функций с параметром подкласса

#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 в функцию, которая ожидает a Child .

2. Нет, это неправильный тип отклонения. См. раздел Ковариация и контравариантность.

3. Есть ли причина, по которой вы не хотите это делать Wrapper func=[](const Parentamp; child) { .. }; ? Вы все равно можете вызвать его с помощью a Child (и его 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); 
}
 

ДЕМОНСТРАЦИЯ