112

C++ 枚举是有符号的还是无符号的?通过扩展来验证输入是否安全,通过检查它是否 <= 您的最大值,并忽略 >= 您的最小值(假设您从 0 开始并递增 1)?

4

10 回答 10

105

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

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

简而言之,您的编译器可以进行选择(显然,如果您的某些枚举值有负数,它将被签名)。

于 2008-10-01T19:31:25.397 回答
64

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

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

于 2008-10-01T18:38:27.567 回答
23

你不应该依赖他们被签名或未签名。如果要使它们显式签名或未签名,可以使用以下内容:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum
于 2008-10-01T18:48:09.133 回答
16

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

在 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.
于 2008-10-02T19:10:15.250 回答
6

即使是一些旧的答案也得到了 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 个枚举操作数时,我们实际上是在讨论将枚举类型隐式转换为某种整数类型。重要的是这种整数类型的符号。并且在将 enum 转换为整数类型时,此语句适用:

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

而且,显然,枚举的底层类型与 Integral Promotion 无关。由于标准定义了这样的积分促销:

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可以包含已定义枚举数的所有值,而不是枚举的基础类型。

请参阅我的相关问题 Sign of C++ Enum Type Incorrect After Converting to Integral Type

于 2014-07-17T12:29:41.767 回答
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.
于 2008-10-01T18:37:55.243 回答
5

将来,在 C++0x 中,强类型枚举将可用并具有几个优点(例如类型安全、显式底层类型或显式范围)。这样您就可以更好地确定类型的符号。

于 2008-10-01T19:05:15.957 回答
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。如果基础类型是有符号整数,则所需的最小位域有 4 位,如果实现中的整数是二进制补码,则有效范围枚举是 -8 到 7。如果基础类型是无符号的,则它有 3 位,范围是 0 到 7。如果您关心,请检查您的编译器文档(例如,如果您想将除枚举数之外的整数值转换为枚举类型,那么您需要知道该值是否在枚举范围内 - 如果不是,则生成的枚举值未指定)。

这些值是否是函数的有效输入可能与它们是否是枚举类型的有效值不同。你的检查代码可能担心的是前者而不是后者,所以在这个例子中至少应该检查 >=A 和 <=B。

于 2008-10-03T17:46:03.183 回答
2

std::is_signed<std::underlying_type使用+ scoped enums default to检查它int

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

主文件

#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 上测试。

于 2019-02-07T11:33:12.307 回答
0

虽然上面的一些答案可以说是正确的,但它们并没有回答我的实际问题。编译器 (gcc 9.3.0) 发出警告:

enum FOO_STATUS {
    STATUS_ERROR = (1 << 31)
};

使用时发出警告:

unsigned status = foo_status_get();
if (STATUS_ERROR == status) {

(除了这个代码不正确的事实......不要问。)

当被正确询问时,编译器不会发出错误。

enum FOO_STATUS {
    STATUS_ERROR = (1U << 31)
};

请注意,这1U会使表达式无符号。

于 2021-09-07T12:54:46.057 回答