如果 Car 是 Vehicle 的子类型,为什么 Vehicle->void 被认为是 Car->void 的子类型?
If Car is a subtype of Vehicle, why is Vehicle->void considered a subtype of Car->void?
正在阅读 a fundamental paper 关于继承的内容,我无法理解下面显示的推理。显然它是正确的,因为逆变确实有效,我只想了解推理。
首先表明:
- 如果函数returns Car,那么它是函数returning Vehicle的子类型.那是因为所有的车辆也是汽车。 (return 类型的协方差)。
- 如果函数接受 Vehicle,那么它是接受 Car[=29] 的函数的 subtype =] 因为“一般来说,车辆上的每个功能也是
汽车。
我无法理解这种反转的解释,所以我在下面展示:
我天真的解释:
由于所有车辆上的功能将适用于所有汽车,因此函数采用车辆是函数采用汽车的子类型,因为 Vehlice 的函数集更小 - 汽车可以有更多的功能。
你的问题是关于函数的协变和逆变,我认为如果我们将论文中一些与语言无关的符号关系映射到实际代码中,将有助于你的理解。在 C++ 中,这里讨论的函数是:
int GetSpeedOf(Vehicle vehicle);
子类型必须按照Liskov-substitution来理解;如果一个函数需要任何类型的动物,你可以给它一只猫,一切都应该正常,但反之则不然;需要猫的功能无法对任何类型的动物起作用。
假设您了解可以将 Car 传递给 GetSpeedOf 函数,现在我们将解决更复杂的函数接受函数的情况,这会带来逆变。
以下 CarWrapper 有一辆私人汽车,它将使用外部提供的功能对其进行操作。该功能必须适用于汽车。因此,如果您提供一个适用于所有 Vehicle 的函数,那很好。
#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>
.因此 "If a function takes Vehicle, then it is a subtype of a function that takes Car"。这是可能的,因为 "Works" 函数将执行的所有操作也必须对实际传入的内容(汽车)也是可能的。
这方面的一个用例是,Works 函数也可以传递给 BoatWrapper class,它需要一个在船上运行的函数。我们可以给出该函数类型的子类型,"Vehicle -> void",知道实际传递的函数中的所有操作也必须在传递给它的 Boat 上可用,因为 Boat 是 Vehicle 的子类型,而我们的实际函数仅使用要操作的参数的更一般的 Vehicle'ness。
另一方面,协方差在 return 类型上运行;如果 CarWrapper class 需要一个为我们生成 Car 的回调,我们将无法传入 Vehicle 生成函数,因为那样 CarWrapper 将无法以特定于汽车的方式使用结果。
如果我们有一个需要车辆发电机的函数,我们就可以给它一个汽车发电机或船发电机;因此 (void -> Car) 是 (void -> Vehicle) 的子类型当且仅当 Car 是 Vehicle 的子类型。
协方差意味着子类型关系保持同一个方向,
所以我们可以继续坚持另一个功能应用程序,并且 "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)))
这意味着如果我们期望一个 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 示例包含协方差的三个应用。
正在阅读 a fundamental paper 关于继承的内容,我无法理解下面显示的推理。显然它是正确的,因为逆变确实有效,我只想了解推理。 首先表明:
- 如果函数returns Car,那么它是函数returning Vehicle的子类型.那是因为所有的车辆也是汽车。 (return 类型的协方差)。
- 如果函数接受 Vehicle,那么它是接受 Car[=29] 的函数的 subtype =] 因为“一般来说,车辆上的每个功能也是
汽车。
我无法理解这种反转的解释,所以我在下面展示:
我天真的解释:
由于所有车辆上的功能将适用于所有汽车,因此函数采用车辆是函数采用汽车的子类型,因为 Vehlice 的函数集更小 - 汽车可以有更多的功能。
你的问题是关于函数的协变和逆变,我认为如果我们将论文中一些与语言无关的符号关系映射到实际代码中,将有助于你的理解。在 C++ 中,这里讨论的函数是:
int GetSpeedOf(Vehicle vehicle);
子类型必须按照Liskov-substitution来理解;如果一个函数需要任何类型的动物,你可以给它一只猫,一切都应该正常,但反之则不然;需要猫的功能无法对任何类型的动物起作用。
假设您了解可以将 Car 传递给 GetSpeedOf 函数,现在我们将解决更复杂的函数接受函数的情况,这会带来逆变。
以下 CarWrapper 有一辆私人汽车,它将使用外部提供的功能对其进行操作。该功能必须适用于汽车。因此,如果您提供一个适用于所有 Vehicle 的函数,那很好。
#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>
.因此 "If a function takes Vehicle, then it is a subtype of a function that takes Car"。这是可能的,因为 "Works" 函数将执行的所有操作也必须对实际传入的内容(汽车)也是可能的。
这方面的一个用例是,Works 函数也可以传递给 BoatWrapper class,它需要一个在船上运行的函数。我们可以给出该函数类型的子类型,"Vehicle -> void",知道实际传递的函数中的所有操作也必须在传递给它的 Boat 上可用,因为 Boat 是 Vehicle 的子类型,而我们的实际函数仅使用要操作的参数的更一般的 Vehicle'ness。
另一方面,协方差在 return 类型上运行;如果 CarWrapper class 需要一个为我们生成 Car 的回调,我们将无法传入 Vehicle 生成函数,因为那样 CarWrapper 将无法以特定于汽车的方式使用结果。
如果我们有一个需要车辆发电机的函数,我们就可以给它一个汽车发电机或船发电机;因此 (void -> Car) 是 (void -> Vehicle) 的子类型当且仅当 Car 是 Vehicle 的子类型。
协方差意味着子类型关系保持同一个方向, 所以我们可以继续坚持另一个功能应用程序,并且 "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)))
这意味着如果我们期望一个 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 示例包含协方差的三个应用。