如果Car是Vehicle的子类型,为什么将Vehicle-> void视为Car-> void的子类型?

时间:2018-10-12 08:58:15

标签: inheritance functional-programming language-agnostic covariance contravariance

阅读a fundamental paper有关继承的信息,我无法理解下面显示的推理。显然这是正确的,因为反差确实有效,我只想了解其原因。 首先,它显示为:

  • 如果函数返回 Car ,则它是返回 Vehicle 的函数的子类型。那是因为所有车辆也是汽车。 (返回类型的协方差)。
  • 如果某个函数采用车辆,则它是采用 Car 的函数的子类型,因为“ 通常每个函数在车辆上也是一个功能 汽车。
    我无法理解这种反转的解释,因此在下面显示: enter image description here

我的天真解释:

由于所有车辆上的功能都将在所有汽车上运行,因此,“乘用车辆”功能是“乘用汽车”功能的子类型,因为“车辆”功能集较小-汽车可以具有更多功能。

1 个答案:

答案 0 :(得分:2)

您的问题是关于函数的协方差和协方差的,如果我们将论文中某些语言不可知的符号关系映射到实际代码,这将对您的理解有所帮助。在C ++中,此处讨论的功能为:

int GetSpeedOf(Vehicle vehicle);

子类型必须用Liskov-substitution来理解;如果某个功能需要任何类型的动物,则可以给它一只猫,并且一切都会正常进行,但是事实并非如此。需要猫的功能无法在任何类型的动物上使用。

假设您了解可以将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”的类型实际上是{{1 }}。因此,“如果一个函数使用了Vehicle,那么它就是一个使用Car的函数的子类型”。之所以可以这样做,是因为“ Works”功能将要执行的所有操作,也必须在实际传入的东西(汽车)上成为可能。

一个用例是,Works函数也可以传递给BoatWrapper类,该类需要在船上运行的函数。我们可以给出该功能类型的子类型“ Vehicle-> void”,知道实际传递的函数中的所有操作也必须在传递给它的Boat上可用,因为Boat是Vehicle的子类型,而我们的实际函数仅使用该参数的操作性越通用。

另一方面,协方差对返回类型进行操作;如果CarWrapper类需要一个回调为我们生成Car,则我们将无法传递Vehicle生成函数,因为CarWrapper无法以汽车特定的方式使用结果。

但是,如果我们有一个期待车辆产生器的功能,那么我们可以给它提供汽车产生器或船用产生器;因此(void-> Car)是(void-> Vehicle)的子类型,前提是Car是Vehicle的子类型。

协方差意味着子类型关系保持相同的方向, 因此我们可以继续使用其他功能应用程序,而“ Car side”仍然是“ Vehicle side”的子类型,即:

std::function<auto(Vehicle) -> void>

这意味着如果我们期望有一个VehicleFactoryFactoryFactory,那么当我们获得CarFactoryFactoryFactory时我们应该满足:

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)))

由于参数类型的矛盾,关系随每个函数应用程序而反转。

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

在有逆差的情况下,很难以直觉的方式理解这一点。这就是为什么我的CarWrapper仅尝试对规则的单个应用解释它的原因,而CarFactory示例包含三个协方差应用。