2014-04-27 131 views
13

根据§7.2/ 5和§7.2/ 6不应该code below打印1 1而不是4 4代码不应该打印“1 1”而不是“4 4”?

#include <iostream> 
enum A { a = (char)1, b, c }; // underlying type is not fixed 

int main() { 
    std::cout << sizeof(a) << ' ' << sizeof(A) << '\n'; 
} 

编辑

从§7.2/ 5:

如果底层类型是不固定的,每个枚举的类型是 其初始化值的类型:

- 如果为枚举器指定了初始值设定项,则初始值 的值与表达式的类型相同并且常数表达式 应该是积分常数表达式(5.19)。

+1

这两个子句都没有说明基础类型必须尽可能小。 –

+0

@OliCharlesworth查看我的编辑 –

+0

我认为“继enum-specifier的大括号后,每个枚举器都有其枚举类型。”从§7.2/ 5也适用于具有非固定类型的枚举。 [现场示例](http://coliru.stacked-crooked.com/a/cc6897fa1bfab56f) – dyp

回答

20

如果您没有明确定义底层类型,那么编译器可以自由选择适合这些值的整型。要设置基础类型的C++ 11你可以使用这个:

enum A : char { a = 1, b, c }; 
     ^^^^^^ 

你的方式不会强迫编译器char,而是如果int使用。

+1

你明白了!很好的回答(+1) –

5

这是实现定义的事实:一个enum的所有值适应,也就是说,一个uint8_t不强制编译器挑选枚举一个单字节表示。

枚举的基础类型是一个整型,它可以表示枚举中定义的所有枚举值。 它是实现定义的,整数类型用作枚举的基础类型,除非基础类型不应大于int,除非枚举器的值不适合int或unsigned int。 (强调)

你的情况看来,编译器执行者选择的int,这需要你的平台上四个字节 - 一个完全有效的选择。

4

您引用的条款7.2/5描述了的列表类型。但是普查员只构成列举定义的一部分。基础类型枚举的是大到足以容纳所有值枚举,受7.2/6:

它是实现定义其积分类型用作底层类型不同的是,基础类型不得大于int,除非枚举数的值不适合intunsigned int

所以可以保证您的基础类型不大于int的情况下(因为int可以表示0,1和2)。确实,您的第一个枚举数的类型在枚举定义中为char,但所有实际的枚举值都是A类型。要实际控制基础类型,请使用enum-base语法(例如enum A : char),并查询它可以使用std::underlying_type特征。

如果你真的想看到枚举的定义中的类型的效果,你可以尝试这样的事:

enum Foo { a = '\010', b = sizeof(a) }; 

std::cout << typeid(b).name() << "\n"; // some variant of "Foo" 
std::cout << b << "\n";     // "1" 
std::cout << sizeof(b) << "\n";   // implementation-defined, not greater 
              // than sizeof(int) 
+0

*“的确,第一个枚举器的类型是'char'”*直到枚举的'}',看到我对OP的评论。 – dyp

+0

@dyp:恩,是的,枚举器不是程序可用的东西。它只是作为枚举类型定义的一部分。 –

+0

咦?/11表示“每个* enum-name *和每个unscoped *枚举器*在立即包含 *枚举说明符*的范围内声明”。据我所知,这些常量是*枚举*,即使您在枚举声明结束后访问它们。 (后来在/ 11“在类范围中声明的枚举数可以使用类成员访问 运算符[......]”) – dyp

4

号从ANSI C开始,合规编译器经常使用int来存储枚举,即使所有的值都很小。

在你说这是疯了之前,它应该使用最小的工作类型(如果你使用__attribute__((packed)),GCC会这样做),考虑一下ABI的兼容性。如果您释放使用枚举类型的库,则希望该类型的大小不会更改。如果所有的枚举都以4个字节开始,那么可能会增加,只需重新链接更新的库就可以工作。

+0

查看我的编辑以上 –

+1

我希望语言设计人员允许程序员根据* requirements *指定类型。如果一个类型需要保持0-50,000的值并且必须紧凑地存储,但是包装行为并不重要,在许多处理器上,当存储在存储器存储器中时最好是“uint16_t”,而当存储在存储器中时最好是“uint32_t”保存在注册表中。如果需要包装mod 4,294,967,296的东西,那么它应该包装mod 4,294,967,296,而不管机器的原始字大小如何。一种语言让程序员指定需求越好,各种使用情况下需要的妥协就越少。 – supercat

相关问题