您可以用派生类/基类替换返回值/参数的想法称为协变返回类型和逆变参数。
在 C++ 中,virtual 指针的引用和指针返回类型在派生类型中具有协变重载;您可以在派生类型中返回一个更受限制(指针或引用)的基返回类型。
C++ 不支持参数的逆变,即用派生的 Base* 参数替换基接口中的 Derived* 参数,但您可以通过重载和覆盖来模拟它。
struct BaseValue {};
struct DerivedValue:BaseValue {};
struct MoreDerivedValue:DerivedValue {};
struct BaseInterface {
virtual void contra_example( DerivedValue* ptr ) = 0;
virtual DerivedValue* co_example() = 0;
virtual ~BaseInterface() {}
};
struct DerivedInterface:BaseInterface {
virtual void contra_example( DerivedValue* ptr ) override final {
contra_example( static_cast<Value*>( ptr ) );
}
void contra_example( Value* ptr ) override = 0;
virtual MoreDerivedValue* co_example() override = 0;
};
co_example 是返回类型协方差的一个示例。如果我们只是根据指针和对类型层次结构的引用进行协变,编译器会自动为我们执行此操作。
contra_example 是参数类型逆变的一个例子。 ptr,contra_example 的参数,在DerivedInterface 的情况下可以是任何Value*。基本接口要求它是DerivedValue*。
我们可以覆盖基础contra_example,然后转发到我们内部的“更接受”实现,这是DerivedInterface 中的一个重载。
派生接口比基接口更宽松,它提供的保证至少与原始接口一样好,或更好。
现在让我们回到您的问题。首先,不,编译器不会为你做。
第二,你的逻辑有缺陷。使用Liskov substitution principle,您的B 必须能够替换A。
但是你的B 对f 的论点比A 有一个更受限制的合同。 A 需要 X 参数,B 不仅需要 X 还需要 Y。
struct X1{};
struct X2{};
struct Y:X1,X2{};
struct A {
virtual void foo( Y* ) = 0;
virtual ~A() {}
}
struct B1:A {
virtual void foo( Y* ptr ) final override { foo( static_cast<X1*>(ptr) ); }
virtual void foo( X1* ) = 0;
}
struct B2:A {
virtual void foo( Y* ptr ) final override { foo( static_cast<X2*>(ptr) ); }
virtual void foo( X2* ) = 0;
}
这对应于您的示例。派生接口对它们的(隐式输入)参数更加宽容。
您可以跳过箍来获得支持协方差的仅输出参数,或者简单地返回它们。