虚函数返回类型的枚举协变

3
为什么C++不能将enum MyEnum : int识别为协变至int
例如:http://ideone.com/Ns1O2d
#include <iostream>

enum FooType : int
{
    Crazy = 0,
    Cool
};

enum BarType : int
{
        Hello = Cool + 1,
        World
};

class Foo
{
public:
        Foo(void)
        {
        }

        ~Foo(void)
        {
        }

        virtual int getType(void) 
        {
                        return Crazy;
        }
};

class Bar : public Foo
{
public:
        Bar(void)
        {
        }

        ~Bar(void)
        {
        }

        virtual BarType getType(void)
        {
                return Hello;
        }
};

int main(int argc, char* argv[])
{
        Bar f = Bar();           
        std::cout << f.getType() << std::endl;
        return 0;
}

编译错误:

prog.cpp:43:18: error: conflicting return type specified for 'virtual BarType Bar::getType()'
prog.cpp:26:14: error:   overriding 'virtual int Foo::getType()'

仅仅通过将 Bar::getType() 的返回类型从 BarType 改为 int(而不做其他任何更改),就可以达到所需的效果,对吗? - jogojapan
1
因为它不是... FooTypeBarType 是不同的类型,如果你想将你的 BarType 强制转换为 int,你可以 return (int)Crazy; - masaers
4个回答

3

非作用域枚举类型(即通常的enums,与enum classenum struct相对)提供了对整数的隐式提升,也就是说你可以这样做:

enum FooType : int { Crazy, Cool };
int val = Crazy; // promotion to integer

然而,这种方法不能反向使用:
FooType val = 0;  // illegal

这是从§7.2/5推导出来的:每个枚举定义了一个与所有其他类型不同的类型,结合§7.2/9:未作用域的枚举类型的枚举器或对象的值通过整数提升转换为整数
我认为这个规则的原因非常明显:可能存在(通常存在)没有对应枚举器定义的整数值。在上面的例子中,理论上可以将01转换,但无法转换2或任何更大的数字。
然而,如果枚举对其底层类型(在您的示例中为int)是协变的,按照您定义的意义,以下操作将成为可能:
class Foo
{
public:
  virtual ~Foo(void) {}
  virtual int getType(void) 
  {
    return Crazy;
  }
};

class Bar : public Foo
{
public:
  virtual ~Bar(void) {}
  virtual BarType getType(void)
  {
    return Foo::getType();
  }
};

在派生类中,Bar::getType() 已经被定义为返回一个 BarType,但这是通过调用继承函数 Foo::getType() 来实现的,这是完全合法的。
如果按照当前的方式进行实现,Bar::getType() 将不得不将从 Foo::getType() 返回的 int 隐式转换为 int。这是不可行的,如上所述。
然而,你仍然可以通过声明与 Foo::getType 相同的方式来实现代码意图,并返回一个 BarType(它会隐式提升为 int):
class Bar : public Foo
{
public:
  virtual ~Bar(void) {}
  virtual int getType(void)
  {
    return Hello;
  }
};

请注意,这仅适用于基础类型为int的情况(因为您在枚举声明中将其修复为int),并且如果枚举不是作用域枚举(即未使用enum classenum struct)。

2

C++并不是这样工作的。协变返回类型的概念仅适用于引用或指向原始返回类型的子类对象的指针。

当您编写 enum class MyEnum : int 时,您并没有指定一个子类,而是指定 MyEnum 将由 int 类型实现。

您只允许执行以下操作:

class Base {
  public:
  virtual Base* foo() = 0;
};

class Derived : public Base {
  public:
  Derived* foo();
};

2

在最近的 C++ 中引入了带类型的 enum。在理想情况下,enum 应该有很多功能 - 我们应该能够遍历定义的值,将它们转换为代数,像 class 一样扩展它们,还可以像您所要求的那样创建返回协变 enumvirtual 函数。

但我们现在做不到。

现在,我们能做到的是以下内容:

class Base {
public:
  virtual int getType() const { return 0; }
};

enum Bird : int { Chicken = 0, Duck = 1 };

class Derived: public Base {
public:
  Bird getBirdType() const { return static_cast<Bird>(getType()); }
  virtual int getType() const override { return Chicken; }
};

旧的签名 (getType) 仍然暴露,而新函数 (getBirdType) 给我们提供了类型校正版本。

如果我们能写出 Bird Derived::getType() const override,那么这个世界可能会更美好,但除非你同意莱布尼兹的观点,否则我们并不生活在那个世界中。向C++添加功能需要标准开发时间,并且编译器实现它需要时间。 因此,被添加到C++中的功能往往是经过市场上编译器测试的,并且其成本值得其收益和需求。

如果你真的想要那个功能,我鼓励你参与C++标准化工作!


2
为什么C++不认为enum MyEnum : intint是协变的?
因为它们不同,尽管派生类对象可以被隐式地视为其公共基类类型的对象,强类型枚举是强类型的。也就是说,它们不能隐式地视为其底层类型的对象,并且因此不是协变候选项。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接