【问题标题】:Why can't I have an enum as the underlying type of another enum?为什么我不能将枚举作为另一个枚举的基础类型?
【发布时间】:2016-11-15 10:05:49
【问题描述】:

为什么这不是有效的 C++?:

enum foo : unsigned { first_foo, second_foo };
enum bar : foo { best_foo = first_foo };

GCC 5.4.0 说:

/tmp/a.cpp:3:16: error: underlying type ‘foo’ of ‘bar’ must be an integral type
     enum bar : foo { best_foo = first_foo };

如果foofloat,或者一些结构,或者其他什么,我可以理解为什么我会得到这个错误。但这在语义、类型安全等方面对我来说似乎完全合法。我错过了什么?

【问题讨论】:

  • 为什么?因为标准是这样说的。 :)
  • @Zereges: But... why?
  • 通常的原因是some people wrote a proposal和他们的提议被接受了。
  • 对我来说,说一个枚举可以基于另一个枚举是一个逻辑错误。如果您认为 base 是存储枚举中定义的值的存储类型,我无法将其作为逻辑表达式来说存储类型是枚举。因为枚举不仅是存储类型,它还包含一组有效值。那么基于其他枚举的枚举应该是什么?只是底层枚举的一个子集,因为所有其他值都没有在底层枚举中定义?为什么枚举不应该基于也代表某种整体存储类型的位域?
  • @Klaus:看看我的动机here。但你提出了一个有效的观点,也许你应该把它作为一个答案。

标签: c++ c++11 enums subclassing


【解决方案1】:

C++11 [dcl.enum]/2:

enum-basetype-specifier-seq 应命名为整数类型;任何 cv 限定都会被忽略。

枚举本身不是整数类型 - [basic.fundamental]/7:

类型boolcharchar16_tchar32_twchar_t,有符号和无符号整数类型统称为整数类型。

附有非规范性脚注:

因此,枚举不是整数;但是,枚举可以提升为 [conv.prom] 中指定的整数类型。

但是,要实现我认为您正在寻找的效果,仍然很简单:

enum bar : std::underlying_type<foo>::type { best_foo = first_foo };

【讨论】:

  • 好的,这就是直接的形式原因 - 但是为什么 type-specifier-seq 必须命名一个整数类型而不是整数类型或枚举类型?
  • @einpoklum :对于这类网站来说,这是一个影响深远的问题。我不是 C++ 委员会的成员,所以我不确定我怎么知道。 ;-]
  • 嗯,有时委员会成员或关注其工作的人会经常访问此站点并回答问题。 Bjarne 甚至回答了我一次。
  • @einpoklum 我怀疑答案是他们认为这并不重要。如果你想改变它,写一个建议:)。
  • 受这个答案的启发,我检查了整数类型的语法。 en.cppreference.com/w/cpp/language/type [整数类型(另见 std::is_integral)]
【解决方案2】:

当您向 C++ 添加内容时,您倾向于添加解决问题的最少数量。

enum A:int 语法可让您准确指定 enum A 存储为整数的方式。这就是它所做的一切,它解决了问题。

enum A:B 其中B 是一个枚举可能有很多含义。 A 可以扩展B,或者A 可以是具有不同名称的B 基础类型的子集,或者A 可以是严格限制为具有可以存储在其中的值的子集的枚举BA 可以是一个枚举,其值被限制为 B 值的“外壳”。

其中,“相同的底层类型”解决方案(使用:int 语法)与enum A:std::underlying_type_t&lt;B&gt; 一致。所以已经有办法做到这一点,而且不是特别繁琐。

除非有人对enum A:B 的含义提出建议并说服委员会足够多,否则不太可能被允许。而当有多种不同的合理含义时,这使得选择任何一种含义变得更加困难。人们需要一个强有力的用例,为什么一个特定的含义是最好的,以及为什么值得努力将其纳入标准。

【讨论】:

  • 好吧,那么,如果你喜欢 - 你可以看看我的 use case 并附上你是否/在多大程度上觉得它引人注目。
【解决方案3】:

嗯,不完全一样,但我认为你可以使用std::underlying_type

enum foo : unsigned { first_foo, second_foo };
enum bar : std::underlying_type_t<foo> { best_foo = first_foo };

【讨论】:

  • @einpoklum 好吧,你没有在你的问题中提到这一点,所以我怎么可能知道?我也不知道为什么它不能满足您的需求;无论如何,常规的enum 在底层类型转换方面非常弱。
【解决方案4】:

对我来说,说一个枚举可以基于另一个枚举是一个逻辑错误。如果您认为 base 是存储枚举中定义的值的存储类型,我无法将其作为逻辑表达式来说存储类型是枚举。因为枚举不仅是存储类型,它还包含一组有效值。

如果我们可以这样写:

enum A: int { ONE, TWO };

应该是什么意思:

enum B: A{};

因为 A 不仅定义了底层类型(我称之为存储类型)而且还定义了一组有效值,所以 B 应该只是枚举 A 的一个子集,这意味着您只能定义 A 中已经定义的值吗?

现在可以说我们已经在 B 中定义了值 ONE、TWO 吗? 现在是否可以添加更多值,例如:

    enum B: A{THREE};

所有有效值现在都是一、二、三?

或者是我们只得到子集的意思:

    enum B: A{ONE};

这意味着 B 只能使用已经在 A 中定义的值。这让我很难将一个枚举作为另一个枚举的基础。

如果你打开那扇门,你也可以想到你想使用其他类型的底层存储类型,比如位域。

struct A { unsigned int a : 3; unsigned int B: 2; };

那么应该

enum B: A { ONE, TWO };

也有效吗?我不信! ;)

从逻辑角度(我的观点)枚举的底层类型是它的存储类型。而且将枚举作为另一个枚举的底层存储类型根本没有任何意义。

【讨论】:

  • “应该只是枚举 A 的子集”?是的,我实际上是这么认为的。
  • @einpoklum 如果只能是子集,则与从结构/类派生相反。因为您只能在派生类中扩展一个基类。那么反转枚举的逻辑是否合乎逻辑? ;)
  • 克劳斯:这不是真的。继承通常用于限制基类而不是扩展它。您在继承的类之间同时具有“Is-a-kind-of which-also”和“is-a-kind-of which-only”关系。
猜你喜欢
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-30
  • 2013-02-11
  • 2010-10-09
  • 1970-01-01
相关资源
最近更新 更多