简体   繁体   English

C ++:在f的派生类实现中,可以将纯虚函数f的参数x替换为x的子类型吗?

[英]C++: Can the parameter x of a pure virtual function f be replaced with a subtype of x in the derived classes implementation of f?

Consider the following abstract class, which will be the interface for a class that writes the information carried by some object to standard output. 考虑下面的抽象类,它将作为一个类的接口,该类将某个对象携带的信息写入标准输出。

class FileBuilder
{
public:                                                                         
    virtual void build(const Object& object) = 0;

    virtual ~FileBuilder() = default;
};

At this point I will note that Object is also an abstract class with derived class SpecialObject . 在这一点上,我将注意到Object也是带有派生类SpecialObject的抽象类。 Now I am going to implement SpecialFileBuilder : FileBuilder , as follows. 现在,我将如下实现SpecialFileBuilder : FileBuilder

class SpecialFileBuilder : public FileBuilder
{
public:                                                                         
    void build(const SpecialObject& specialObject);
};

...

void SpecialFileBuilder::build(const SpecialObject& specialObject)
{
    // Do some stuff
}

I don't fully understand why this should not be possible. 我不完全理解为什么这不可能。 SpecialFileBuilder respects the interface FileBuilder , and everywhere which expects a FileBuilder can instead be given a SpecialFileBuilder . SpecialFileBuilder尊重FileBuilder的接口, FileBuilder希望FileBuilder都可以改用SpecialFileBuilder I appreciate your help in advance. 非常感谢您的帮助。

Of course, this would work if I changed things to the following. 当然,如果我将内容更改为以下内容,则可以使用。

void SpecialFileBuilder::build(const Object& object)

However, in my implementation of SpecialFileBuilder::build() I need to use the fact that the argument is a SpecialObject , not just an Object . 但是,在我实现SpecialFileBuilder::build()我需要使用以下事实:参数是SpecialObject ,而不仅仅是Object

How should I instead approach this design? 我应该如何进行这种设计?

TL;DR no, this does not make any sense. TL; DR不,这没有任何意义。

Full version below. 完整版本如下。

I don't fully understand why this should not be possible. 我不完全理解为什么这不可能。

virtual void build(const Object& object) = 0;

This declaration is a promise . 这个声明是一个承诺 It promises that build can accept any Object as an argument. 它承诺build可以接受任何 Object作为参数。 Such promises are legally binding for derived classes, ie they must implement the promise as stated by the base class. 此类承诺对派生类具有法律约束力,即它们必须实现基类所声明的承诺。 Note the declaration does not promise that build can accept some objects and not others. 请注意,声明并不保证build可以接受某些对象,而不能接受其他对象。

FileBuilder* builder = GetBuilder(); // we don't know what kind of builder it is

SpecialObject some;
builder->build(some); // must work

OtherSpecialObject some;
builder->build(other); // must work too

UnrelatedObject whatever;
builder->build(whatever); // must work as well

Now looking at the other declaration 现在看另一个声明

void build(const SpecialObject& specialObject);

It reneges on the promise. 它违背了诺言。 The original promise is strong. 最初的承诺是坚定的。 Give me any object, I can deal with it. 给我任何东西,我可以处理。 The new promise is weak. 新的承诺是微弱的。 Oh, I am a special little builder, I can only cope with special little objects! 哦,我是一个特殊的小建造者,我只能应付特殊的小物件!

Sorry bud, you cannot override a strong promise with a weaker one. 抱歉,您不能用较弱的承诺来取代一个有力的承诺。 If you were allowed to, how would we be able to trust any promise? 如果您被允许,我们将如何能够兑现任何承诺?

Now if your design doesn't fit in this outline, ie you always know what kind of builder you get, and you don't want to promise to cope with all kinds of objects, then you have selected a wrong tool for the job. 现在,如果您的设计不符合该大纲,即您始终知道您会获得哪种类型的生成器,并且您不想承诺应对所有类型的对象,那么您就选择了错误的工具来完成这项工作。 Perhaps you want to give generic programming a try. 也许您想尝试一下通用编程。

template <typename T>
class FileBuilder {
   virtual void build (const T& t) = 0;
};

class SpecialBuilder:  public FileBuilder<SpecialObject> {
   void build (const SpecialObject& t) override;
};

Now the code above won't work, we need to fix it 现在上面的代码将无法正常工作,我们需要对其进行修复

FileBuilder<SpecialObject>* builder = GetBuilder<SpecialObject>(); // we know exactly what we want to build

SpecialObject some;
builder->build(some); // will work;

OtherSpecialObject other;
builder->build(other); // sorry that's not in the contract, won't compile

I don't fully understand why this should not be possible. 我不完全理解为什么这不可能。 SpecialFileBuilder respects the interface FileBuilder, and everywhere which expects a FileBuilder can instead be given a SpecialFileBuilder SpecialFileBuilder尊重FileBuilder的接口,凡是希望FileBuilder的地方都可以改用SpecialFileBuilder

You may have covariant return type. 您可能具有协变返回类型。

But for argument, you would need contra-variant return type (which is not supported in C++). 但是对于参数,您将需要反变量返回类型(C ++不支持)。

As following code should be correct 由于以下代码应该是正确的

SpecialFileBuilder specialFileBuilder;
FileBuilder& fileBuilder;
SpecialObject2 specialObject2; // Other derived class, unrelated to SpecialObject
Object& object = specialObject2;

fileBuilder.build(object); // correct type
// but
specialFileBuilder.build(specialObject2); // won't compile

contra-variant parameter would be 反变量参数为

struct Base {
    virtual void f(const Cat&) = 0;
};

struct Derived : Base
{
    void f(const Animal&) override; // if contra-variance was supported
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM