【问题标题】:Virtual function return type虚函数返回类型
【发布时间】:2013-04-07 18:59:05
【问题描述】:

我在一个基类中定义了纯虚函数 虚拟int GetData() const = 0;

在每个派生类中,我定义了一个枚举并尝试覆盖 GetData 函数返回 (派生类特定枚举)值;

例如:

class Derived1 : public Base
{
public :
enum D1
{
   d1_1 = 0,
   d1_2 = 60,
   ...
   d1_100
};
D1 GetData () const;
};
class Derived2 : public Base
{
public :
enum D2
{
   d2_1 = 10,
   d2_2 = 39,
   ...
   d2_300
};
D2 GetData () const;
};

说我不能为所有类的所有枚举值定义相同的范围是非常重要的。 上面的代码会产生编译错误:

error C2555: : overriding virtual function return type differs and is not covariant

任何建议 - 如何解决?

【问题讨论】:

  • 在任何地方都将返回类型更改为int
  • 这不是很好,因为保留和使用枚举对于其他功能很重要
  • 它的小缺点是它没有意义,虽然:-(
  • 枚举只是定义整数常量的一种方式,类型名称没有任何意义。将返回类型更改为 int 不会以任何方式影响程序。

标签: c++ overriding covariant


【解决方案1】:

在您的特定情况下,事实是您的 virtual 方法返回一个原始类型,该类型没有协方差,因为它不能放入 C# 中的 System.Object 等常见类型。
您需要定义一个类作为所有返回类型的基类,以实现协方差。

来自维基百科:

Within the type system of a programming language, covariance and contravariance refers to the
ordering of types from narrower to wider and their interchangeability or equivalence in certain 
situations (such as parameters, generics, and return types).

covariant: converting from a specialized type (Cats) to a more general type (Animals): 
Every cat is an animal.

Here是文章的链接。

【讨论】:

    【解决方案2】:

    仅当您将基类的指针/引用替换为派生类的指针/引用时,才允许更改虚函数的返回类型,因为可以安全地将一个指针/引用转换为另一个。虽然枚举类型和int 是兼容的,但它们在技术上并不相关。只需在任何地方使用int,因为枚举名称只是一种装饰,根本不会影响任何东西。

    【讨论】:

    • -1 “不允许更改虚函数的返回类型,没有意义”不正确(C++支持协变原始指针和引用函数结果)。
    • 我刚刚意识到这一点,并且已经修复了,尽管它可能不是提问者想要的。
    • 好的,我正在删除反对票。但请注意,修订中的逻辑并没有真正起作用。现在事实已经确定,推理无效(旧的 C++03 枚举隐式转换为 int,就像派生指针隐式转换为基类指针一样)。
    • 指针/引用协变的要点是,您可以调用 Base 方法期望获得 BaseData 类型的指针,并允许 Derived 类进一步指定返回的对象将实际上是继承自BaseDataDerivedData 类型。这对枚举没有意义,因为它们不支持继承。
    • 正确。但我感觉你认为这与我写的有关。不是。
    【解决方案3】:

    您的设计需要修复,但仅考虑技术您可以做的

    class Derived1
        : public Base
    {
    public:
        enum D1
        {
           d1_1 = 0,
           d1_2 = 60,
           ...
           d1_100
        };
        D1 GetD1Data () const;
        int GetData() const override { return GetD1Data(); }
    };
    

    【讨论】:

    • 此解决方案产生另一个编译错误重载函数仅返回类型不同...
    • @Yakov:修复你的错误。很抱歉,但无法访问您的代码,我无法确切看到您的代码有什么问题。我可以猜测但是你无意中给了这两个函数同名?
    • 也许你可以添加一个明确的int enum-base
    【解决方案4】:

    根据 C++11 ISO 10.3/7:

    重写函数的返回类型应与重写函数的返回类型相同,或者与函数的类协变。如果函数 D::f 覆盖函数 B::f,则如果满足以下条件,则函数的返回类型是协变的

    ——都是类的指针,都是类的左值引用,或者都是类的右值引用

    ——B::f 的返回类型中的类与 D::f 的返回类型中的类相同,或者是返回类型中的类的明确且可访问的直接或间接基类的 D::f

    ——指针或引用都具有相同的 cv 限定,并且 D::f 的返回类型中的类类型具有与 B 的返回类型中的类类型相同或更少的 cv 限定: :f.

    协方差只允许用于指针、左值/右值引用。我猜你不想通过引用或指针返回枚举。

    但是,如果您接受静态线程本地缓冲区,您可以使用以下方法:

    LIVE DEMO

    class EnumA
    {
        int value_;
    public:
        explicit EnumA(int v)
            : value_{v}
        {}
        int value() const
        {
            return value_;
        }
    };
    struct EnumB: EnumA
    {
        enum EnumB_T{one,two};
        explicit EnumB(EnumB_T v)
            : EnumA{v}
        {}
        EnumB_T value() const
        {
            return EnumB_T(EnumA::value());
        }
    };
    
    struct A
    {
        virtual const EnumA &func() const
        {
            static thread_local EnumA result{0};
            return result = EnumA{1};
        }
    };
    
    struct B: A
    {
        virtual const EnumB &func() const override
        {
            static thread_local EnumB result{EnumB::one};
            return result = EnumB{EnumB::two};
        }
    };
    

    【讨论】:

      猜你喜欢
      • 2011-06-07
      • 2023-01-20
      • 2011-07-01
      • 2012-10-28
      • 1970-01-01
      • 1970-01-01
      • 2015-06-21
      • 1970-01-01
      相关资源
      最近更新 更多