Если Car является подтипом Vehicle, почему Vehicle-> void считается подтипом Car-> void? - PullRequest
0 голосов
/ 12 октября 2018

Читая фундаментальный документ о наследовании, я не могу понять рассуждения, показанные ниже.По-видимому, это правильно, так как контравариантность работает, я просто хотел бы понять аргументацию.Сначала показано, что:

  • Если функция возвращает Автомобиль , то это подтип функции, возвращающей Автомобиль .Это потому, что все транспортные средства также являются автомобилями.(Ковариантность возвращаемых типов).
  • Если функция принимает Vehicle , то это подтип функции, которая принимает Car потому что " В общем, каждая функция на транспортных средствах также является функцией на автомобилях."
    Я не могу понять объяснение этой инверсии, поэтому я показываю это ниже: enter image description here

Моя наивная интерпретация:

Поскольку функции на ВСЕХ транспортных средствах будут работать на ВСЕХ автомобилях, то функция получения Транспортного средства является подтипом функции, принимающей Автомобиль, поскольку наборфункции Vehlice меньше - автомобиль может иметь больше функций.

1 Ответ

0 голосов
/ 12 октября 2018

Ваш вопрос касается ковариации и контравариантности функций, и я думаю, что это поможет вам понять, если мы сопоставим некоторые не зависящие от языка символические отношения в статье с реальным кодом.В C ++ обсуждаемая здесь функция будет выглядеть так:

int GetSpeedOf(Vehicle vehicle);

Подтип должен пониматься в терминах Лисковская замена ;если функция ожидает какого-либо типа животного, вы можете дать ему кошку, и все должно работать нормально, но это не так;функция, требующая кота, не может работать на любом типе животных.

Предполагая, что вы понимаете, что можно передать Car в функцию GetSpeedOf, теперь мы рассмотрим более сложный случай принятия функций функциями, которыевносит противоречивость в картину.

У следующего CarWrapper есть личный автомобиль, с которым он будет делать вещи, используя функцию, предоставляемую извне.Эта функция должна работать для автомобилей.Так что, если вы дадите функцию, которая работает более широко для всех транспортных средств, это нормально.

#include <functional>

class Vehicle { };
class Car : public Vehicle { };

class CarWrapper
{
    Car car;
    typedef std::function<auto(Car) -> void> CarCallback;
    CarCallback functionPtr;

public:
    void PassCallback(CarCallback cb)
    {
        functionPtr = cb;
    }

    void DoStuff()
    {
        functionPtr(car);
    }
};

void Works(Vehicle v){}

int main() {
    CarWrapper b;
    b.PassCallback(&Works);
}

Итак, функция PassCallback ожидает тип CarCallback ("Car -> void" в вашей статье), но мы даемэто подтип «Vehicle -> void», потому что тип «& Works» на самом деле std::function<auto(Vehicle) -> void>.Следовательно, «если функция принимает Автомобиль, то это подтип функции, которая принимает Автомобиль».Это возможно, потому что все операции, которые будет выполнять функция «Works», также должны быть возможны для того, что фактически передается в автомобиле.

Вариант использования этого заключается в том, что функция Works можеттакже будет передан в класс BoatWrapper, который ожидает функцию, работающую на лодках.Мы можем дать подтип этого типа функции «Vehicle -> void», зная, что все операции в фактической переданной функции также должны быть доступны на переданной ей лодке, поскольку Boat является подтипом Vehicle, а наша фактическая функция использует толькочем более общий параметр Vehicle для параметра.

Ковариантность, с другой стороны, влияет на тип возвращаемого значения;если бы класс CarWrapper ожидал обратного вызова, который генерирует для нас Car, мы не смогли бы передать функцию создания транспортного средства, потому что тогда CarWrapper не смог бы использовать результат для конкретных автомобилей.

Если бы у нас была функция, ожидающая генератор транспортного средства, мы могли бы предоставить ему генератор автомобилей или генератор лодок;следовательно (void -> Car) является подтипом (void -> Vehicle), если Car является подтипом Vehicle.

Ковариация подразумевает, что отношение подтипа остается в том же направлении, поэтому мы можем продолжать придерживаться в другом приложении функции, и все же «Сторона автомобиля» будет подтипом «Стороны автомобиля», то есть:

Car is a subtype of Vehicle means that:
(void -> Car) is a subtype of (void -> Vehicle) - as in the code sample above
(void -> (void -> Car)) is a subtype of (void -> (void -> Vehicle))
(void -> (void -> (void -> Car))) is a subtype of (void -> (void -> (void -> Vehicle)))

Это означает, что, если мы ожидали, что VehicleFactoryFactoryFactory будет удовлетворен, если нам будет предоставлен CarFactoryFactoryFactory:

#include <functional>

class Vehicle { };
class Car : public Vehicle { };

typedef std::function<auto() -> Vehicle> VehicleFactory;
typedef std::function<auto() -> VehicleFactory> VehicleFactoryFactory;
typedef std::function<auto() -> VehicleFactoryFactory> VehicleFactoryFactoryFactory;

void GiveMeAFactory(VehicleFactoryFactoryFactory factory)
{
    Vehicle theVehicle = factory()()();
}

typedef std::function<auto() -> Car> CarFactory;
typedef std::function<auto() -> CarFactory> CarFactoryFactory;
typedef std::function<auto() -> CarFactoryFactory> CarFactoryFactoryFactory;

Car ActualCarCreateFunc() { return Car(); }
CarFactory CarFactoryCreateFunc() { return &ActualCarCreateFunc; }
CarFactoryFactory CarFactoryFactoryCreateFunc() { return &CarFactoryCreateFunc; }

int main() {
    GiveMeAFactory(&CarFactoryFactoryCreateFunc);
}

При несовпадении типов параметров отношение инвертируется с каждым приложением функции.

Car is a subtype of Vehicle means that:
(Vehicle -> void) is a subtype of (Car -> void)
((Car -> void) -> void) is a subtype of ((Vehicle -> void) -> void)
(((Vehicle -> void) -> void) -> void) is a subtype of (((Car -> void) -> void) -> void)

В случае контравариантности очень трудно понять это в интуитивных терминах.Вот почему мой CarWrapper пытается объяснить его только для одного применения правила, в то время как пример CarFactory содержит три применения ковариации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...