C ++枚举是签名的还是未签名的?


107

C ++枚举是签名的还是未签名的?通过扩展,可以安全地通过检查输入是否等于或小于最小值(假设您从0开始并增加1)来验证输入是否安全?


当我们在需要它的符号的上下文中使用枚举类型时,我们实际上是在谈论将枚举隐式转换为整数类型。C ++ 03标准说这是由Integral Promotion完成的,与枚举的基础类型无关。因此,我不明白为什么每个答案都提到标准未定义底层类型?我这里所描述的预期行为调查:stackoverflow.com/questions/24802322/...
JavaMan

Answers:


60

您不应该依赖任何特定的表示形式。阅读以下链接。此外,该标准还说,由实现定义的是,哪种整数类型用作枚举的基础类型,除非它不得大于int,除非某些值不能适合int或unsigned int。

简而言之:您不能依赖于已签名或未签名的枚举。


28
迈克尔·伯尔Michael Burr)的答案(引用标准)实际上意味着,如果您将枚举值定义为负数,则可以依靠它进行签名,因为该类型能够“表示枚举中定义的所有枚举器值”。
塞缪尔·哈默

101

让我们去探源。这是C ++ 03标准(ISO / IEC 14882:2003)文档在7.2-5(枚举声明)中所说的:

枚举的基础类型是整数类型,可以表示枚举中定义的所有枚举器值。由实现方式定义,哪种整数类型用作枚举的基础类型,除非基础类型不得大于int,除非枚举器的值不能适合int或unsigned int。

简而言之,编译器可以选择(很明显,如果某些枚举值的值为负数,它将被签名)。


当所有枚举值都是小的正整数时,我们如何避免编译器猜测并告诉它使用基础无符号类型?(我们之所以能抓住UBsan的发现,是因为编译器正在选择一个int,而int则遭受溢出。该值是无符号且为正,并且我们的使用取决于无符号包装以提供减量或“负跨度”)。
jww

@jww-我想这取决于您使用的编译器。由于该标准不规定底层类型,而是将其留给实现,因此需要查看工具的文档,并查看该选项是否完全可行。如果要保证代码中的某些行为,为什么不转换在表达式中使用的枚举成员呢?
ysap


15

您不应该依赖于已签名或未签名。根据标准,实现是定义哪种整数类型用作枚举的基础类型。但是,在大多数实现中,它是一个有符号整数。

在C ++ 0x中,将添加强类型枚举,这将允许您指定枚举的类型,例如:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

但是,即使到现在,也可以通过将枚举用作变量或参数类型来实现一些简单的验证,如下所示:

enum Fruit { Apple, Banana };

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit
                    // even though it has the same value as banana.

我认为您的第二个例子有些困惑:)
Miral

5

编译器可以确定枚举是有符号的还是无符号的。

验证枚举的另一种方法是将枚举本身用作变量类型。例如:

enum Fruit
{
    Apple = 0,
    Banana,
    Pineapple,
    Orange,
    Kumquat
};

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit even though it has the same value as banana.

5

即使是一些旧的答案也有44票赞成票,我倾向于不同意所有这些票。简而言之,我认为我们不应该关心underlying type枚举的。

首先,C ++ 03 Enum类型是其自身的独特类型,没有符号的概念。从C ++ 03标准开始dcl.enum

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

因此,当我们谈论枚举类型的符号时,例如,使用<运算符比较2个枚举操作数时,实际上是在隐式地将枚举类型转换为某种整数类型。重要的是这种整体类型的标志。当将枚举转换为整数类型时,此语句适用:

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

而且,显然,枚举的基础类型与“积分提升”无关。由于该标准对积分促销的定义如下:

4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.

因此,枚举类型是否变为signed intunsigned int取决于是否signed int可以包含已定义枚举数的所有值,而不是枚举的基础类型。

看到我相关的问题 转换为整数类型后C ++枚举类型的符号不正确


使用进行编译时很重要-Wsign-conversion。我们使用它来帮助捕获代码中意外的错误。但是+1是引用标准,并指出一个枚举没有与之关联的类型(signedvs unsigned)。
jww

4

将来,随着C ++ 0x的出现,强类型枚举将可用并具有多个优点(例如类型安全,显式基础类型或显式作用域)。这样,您可以更好地确定类型的标志。


4

除了其他人已经说过的关于有符号/无符号的内容之外,以下是该标准对枚举类型范围的说明:

7.2(6):“对于其中e(min)是最小的枚举数且e(max)是最大的枚举,该枚举的值是从b(min)到b(max)范围内的基础类型的值),其中b(min)和b(max)分别是可以存储e(min)和e(max)的最小位字段的最小和最大值。可以定义一个未定义值的枚举通过其任何枚举​​器。”

因此,例如:

enum { A = 1, B = 4};

定义一个枚举类型,其中e(min)为1,e(max)为4。如果基础类型为有符号int,则所需的最小位域为4位,并且如果实现中的int为二进制补码,则有效范围为枚举是-8到7。如果基础类型是无符号的,则它有3位,范围是0到7。如果需要,请查看编译器文档(例如,如果要将除枚举器以外的整数值强制转换为枚举类型,那么您需要知道该值是否在枚举范围内-如果不是,则未指定结果枚举值)。

这些值是否为函数的有效输入,可能不同于它们是否为枚举类型的有效值。您的检查代码可能担心前者而不是后者,因此在此示例中至少应检查> = A和<= B。


0

std::is_signed<std::underlying_type+作用域枚举检查它,默认为int

https://en.cppreference.com/w/cpp/language/enum表示:

main.cpp

#include <cassert>
#include <iostream>
#include <type_traits>

enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};

int main() {
    // Implementation defined, let's find out.
    std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;

    // Guaranteed. Scoped defaults to int.
    assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));

    // Guaranteed. We set it ourselves.
    assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}

GitHub上游

编译并运行:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

输出:

0

已在Ubuntu 16.04,GCC 6.4.0上测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.