【问题标题】:Force a derived class to override one of a set of virtual functions强制派生类覆盖一组虚函数中的一个
【发布时间】:2021-06-14 22:36:53
【问题描述】:

给定一个具有一些虚函数的基类,任何人都可以想出一种方法来强制派生类在编译时重写一组虚函数中的一个吗?或者实现相同目的的类层次结构的替代表述?

在代码中:

struct Base
{
    // Some imaginary syntax to indicate the following are a "pure override set"
    // [
    virtual void function1(int) = 0;
    virtual void function2(float) = 0;
    // ...
    // ]
};

struct Derived1 : Base {}; // ERROR not implemented
struct Derived2 : Base { void function1(int) override; }; // OK
struct Derived3 : Base { void function2(float) override; }; // OK

struct Derived4 : Base // ERROR too many implemented
{
    void function1(int) override;
    void function2(float) override;
};

我不确定我是否真的有一个实际的用例,但是当我正在实施一些松散地遵循这种模式的东西时,我突然想到这是一个值得思考的有趣问题,如果没有其他的话。

【问题讨论】:

  • Derivedx 应该由非抽象子类进一步派生?
  • 没有办法,C++ 不能这样工作。
  • 我知道C++不能这样工作,代码只是为了说明问题。
  • 说明什么问题?您是否进一步从 Derivedxy 类派生?具体的类会是什么样子?
  • 为了说明帖子中概述的问题,有什么不清楚吗?不,我不想进一步推导。

标签: c++ inheritance overriding virtual-functions pure-virtual


【解决方案1】:

如果您不覆盖所有抽象虚拟方法,您的类将保持抽象。如果你想实例化对象,你必须做所有这些。

【讨论】:

  • 是的,我知道。伪代码只是为了说明问题。
【解决方案2】:

不,但你可以伪造它。

Base 具有非虚拟 float 和 int 方法,它们转发到纯虚拟 std 变体之一。

两个帮助类,一个 int 一个 float,实现 std 变体之一,将两种情况转发到纯虚拟 int 或 float 实现。

它负责处理“错误类型”的情况。

Derived 继承自一个或另一个助手,并且仅实现 int 或 float。

struct Base
{
    void function1(int x) { vfunction(x); }
    void function2(float x) { vfunction(x); }
    virtual void vfunction(std::variant<int,float>) = 0;
};
struct Helper1:Base {
    void vfunction(std::variant<int,float> v) final {
      if (std::holds_alternative<int>(v))
        function1_impl( std::get<int>(v) );
    }
    virtual void function1_impl(int x) = 0;
};
struct Helper2:Base {
    void vfunction(std::variant<int,float> v) final {
      if (std::holds_alternative<float>(v))
        function2_impl( std::get<float>(v) );
    }
    virtual void function2_impl(float x) = 0;
};

struct Derived1 : Base {}; // ERROR not implemented
struct Derived2 : Helper1 { void function1_impl(int) override; }; // OK
struct Derived3 : Helper2 { void function2_impl(float) override; }; // OK

这使用https://en.wikipedia.org/wiki/Non-virtual_interface_pattern——接口包含非虚拟方法,可以覆盖其细节以使它们表现不同。

如果您担心人们会覆盖 vfunction,您可以使用私有锁定技术,和/或直接将其命名为 private_implementation_detail_do_not_implement,并相信您的代码审查流程。

【讨论】:

  • 酷,这很不错。它假设只有在覆盖集中的函数具有类似的签名时才真正起作用。例如你不能有 function1(int, int, int) 和 function2(string) 因为虚函数的变体参数只能区分单一类型。我的意思是我猜你可以使用一个元组来表示一个参数包 (variant, string>) 但在这种情况下它变得非常可怕。总之,不错。
  • @TimAngus 我认为这已经很可怕了。 :) 所以元组了!在任何情况下,vfunction 实际上都不是必需的:function1 的 NVI 可以调用virtual function1_private_implementation_detailHelper1 可以覆盖这两个实现细节,并将允许使用的那个转发给function1_impl。重点是 Helper1 实现了 policy 以进行覆盖。
【解决方案3】:

或者实现相同目的的类层次结构的替代表述?

一种选择是拥有一个实现一个功能的中间基类。

struct Base
{
    virtual ~Base() {};
    virtual void function(int) = 0;
    virtual void function(float) = 0;
};

template <typename T>
struct TBase : Base
{
   virtual void function(T) override {} 
};

struct Derived1 : Base {};
struct Derived2 : TBase<float> { void function(int) override {} };
struct Derived3 : TBase<int> { void function(float) override {} };

int main()
{
   Derived1 d1; // ERROR. Virtual functions are not implemented
   Derived2 d2; // OK.
   Derived3 d3; // OK.
}

请注意,在这种方法中,函数被命名为 function,而不是 function1function2

【讨论】:

  • 我喜欢这样,当然对于函数重载的情况。起初我认为这会导致问题,例如d3.function(int) 仍然可以被调用(无效),但是继承的重载当然是隐藏的,因为 Derived3 本身重载了函数。一种可能的改进是将“typename T”替换为参数包“typename ... Args”,从而使其适用于采用多个参数的重载。
猜你喜欢
  • 2015-10-23
  • 2018-12-23
  • 1970-01-01
  • 2020-12-25
  • 2014-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多