代码之家  ›  专栏  ›  技术社区  ›  John V

如果汽车是汽车的一个子类型,为什么汽车->无效被认为是汽车->无效的一个子类型?

  •  2
  • John V  · 技术社区  · 6 年前

    阅读 a fundamental paper 关于继承,我无法理解下面的推理。显然,这是正确的,因为相反的工作,我只想理解的推理。 首先,它表明:

    • ,则是函数返回的一个子类型 车辆 . 这是因为所有的车辆也是汽车(回报类型的协方差)。
    • 如果函数 车辆 ,那么它就是一个 子类型 一个函数的 小型车 因为” 一般来说,车辆上的每个功能也是 汽车。 "
      我无法理解这种倒置的解释,所以我在下面展示: enter image description here

    我天真的解释:

    由于所有车辆上的功能将在所有车辆上工作,因此功能执行车辆是功能执行车辆的一个子类型,因为车辆的功能集较小-车辆可以具有更多功能。

    1 回复  |  直到 6 年前
        1
  •  2
  •   TamaMcGlinn    6 年前

    你的问题是关于函数的协方差和逆方差,如果我们把论文中的一些语言不可知的符号关系映射到实际的代码中,我想这将有助于你的理解。在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);
    }
    

    std::function<auto(Vehicle) -> void> . 因此,“如果一个函数占用了Vehicle,那么它就是占用Car的函数的一个子类型”。这是可能的,因为“Works”函数将执行的所有操作,也必须在实际传入的内容上是可能的-一辆车。

    这种情况的一个用例是,Works函数也可以传递给BoatWrapper类,该类需要一个在船上操作的函数。我们可以给出函数类型的子类型,“Vehicle->void”,因为Boat是Vehicle的一个子类型,我们的实际函数只使用参数中更一般的Vehicle属性来操作,所以知道实际传递函数中的所有操作也必须在传递给它的Boat上可用。

    另一方面,协方差对返回类型起作用;如果CarWrapper类需要一个回调来为我们生成一辆汽车,我们就不能传入一个Vehicle生成函数,因为这样CarWrapper就不能以特定于汽车的方式使用结果。

    如果我们有一个功能,期待一个车辆发电机,虽然,我们将能够给它一个汽车发电机或船发电机;因此(无效->Car)是(void->车辆)敌我识别车是车辆的一个子类型。

    协方差意味着子类型关系保持在同一方向, 因此,我们可以继续使用另一个函数应用程序,“Car side”仍然是“Vehicle side”的一个子类型,即:

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

    随着参数类型的相反变化,关系将随着每个函数的应用而反转。

    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)