Các enums trong C ++ có dấu hay không dấu?


107

Các enums trong C ++ có dấu hay không dấu? Và bằng cách mở rộng có an toàn để xác thực đầu vào bằng cách kiểm tra xem nó có phải là <= giá trị tối đa của bạn không và bỏ đi> = giá trị tối thiểu của bạn (giả sử bạn bắt đầu từ 0 và tăng lên 1)?


Khi chúng ta đang sử dụng một kiểu enum trong một ngữ cảnh yêu cầu dấu của nó, chúng ta thực sự đang nói về việc chuyển đổi enum thành một kiểu tích phân một cách ngầm định. Tiêu chuẩn C ++ 03 cho biết điều này được thực hiện bởi Integral Promotion, không có gì liên quan đến kiểu cơ bản của enum. Vì vậy, tôi không hiểu tại sao mọi câu trả lời ở đây đề cập đến loại cơ bản không được định nghĩa bởi tiêu chuẩn? Tôi đã mô tả hành vi mong đợi của bạn tại đây: stackoverflow.com/questions/24802322/…
JavaMan

Câu trả lời:


60

Bạn không nên dựa vào bất kỳ đại diện cụ thể nào. Đọc liên kết sau đây . Ngoài ra, tiêu chuẩn nói rằng nó được thực thi xác định kiểu tích phân nào được sử dụng làm kiểu cơ bản cho một enum, ngoại trừ nó không được lớn hơn int, trừ khi một số giá trị không thể vừa với int hoặc int không dấu.

Tóm lại: bạn không thể dựa vào một enum được ký hoặc không được ký.


28
Câu trả lời của Michael Burr (trích dẫn tiêu chuẩn) thực sự ngụ ý rằng bạn có thể dựa vào việc nó được ký nếu bạn xác định một giá trị enum là âm do kiểu có thể "đại diện cho tất cả các giá trị được xác định trong kiểu liệt kê".
Samuel Harmer

101

Hãy đi về nguồn. Đây là những gì tài liệu tiêu chuẩn C ++ 03 (ISO / IEC 14882: 2003) nói trong 7.2-5 (Khai báo liệt kê):

Kiểu cơ bản của kiểu liệt kê là kiểu tích phân có thể đại diện cho tất cả các giá trị của kiểu liệt kê được xác định trong kiểu liệt kê. Nó được thực thi xác định kiểu tích phân nào được sử dụng làm kiểu cơ bản cho một kiểu liệt kê ngoại trừ kiểu cơ bản không được lớn hơn int trừ khi giá trị của một kiểu tích phân không thể vừa với int hoặc int không dấu.

Tóm lại, trình biên dịch của bạn có thể chọn (rõ ràng, nếu bạn có số âm cho một số giá trị liệt kê của mình, nó sẽ được ký).


Làm thế nào chúng ta có thể tránh các phỏng đoán của trình biên dịch và yêu cầu trình biên dịch sử dụng kiểu không dấu bên dưới khi tất cả các giá trị liệt kê là số nguyên dương nhỏ? (Chúng tôi đang bắt được một phát hiện UBsan bởi vì trình biên dịch đang chọn một int và int bị tràn. Giá trị không có dấu và dương, và việc sử dụng của chúng tôi phụ thuộc vào dấu bọc không dấu để cung cấp giảm hoặc "bước âm").
jww

@jww - điều đó sẽ phụ thuộc vào trình biên dịch chính xác mà bạn đang sử dụng, tôi đoán vậy. Vì tiêu chuẩn không chỉ định loại cơ bản và để điều này thực hiện, do đó cần phải xem tài liệu của công cụ của bạn và xem liệu tùy chọn này có khả thi không. Nếu bạn muốn đảm bảo một hành vi nhất định trong mã của mình, tại sao không ép kiểu thành viên enum mà bạn sử dụng trong biểu thức?
ysap

22

Bạn không nên phụ thuộc vào việc họ đã ký hay chưa ký. Nếu bạn muốn làm cho chúng có chữ ký rõ ràng hoặc không dấu, bạn có thể sử dụng cách sau:

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

11
Chỉ trong tiêu chuẩn C ++ 0x trong tương lai.
dalle 01-08

3
@dalle Microsoft compilator cũng cho phép enums gõ msdn.microsoft.com/en-us/library/2dzy4k6e(v=vs.80).aspx
teodozjan

15

Bạn không nên dựa vào việc nó đã được ký hoặc chưa được ký. Theo tiêu chuẩn, việc triển khai được xác định kiểu tích phân nào được sử dụng làm kiểu cơ bản cho một enum. Tuy nhiên, trong hầu hết các triển khai, nó là một số nguyên có dấu.

Trong C ++, 0x các kiểu liệt kê được đánh máy mạnh sẽ được thêm vào, điều này cho phép bạn chỉ định kiểu của một enum chẳng hạn như:

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

Mặc dù vậy, ngay cả bây giờ, một số xác nhận đơn giản có thể đạt được bằng cách sử dụng enum làm kiểu biến hoặc tham số như sau:

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.

Tôi nghĩ rằng ví dụ thứ hai của bạn là một chút nhầm lẫn :)
MIRAL

5

Trình biên dịch có thể quyết định xem các enums được ký hay không có dấu.

Một phương pháp xác thực enum khác là sử dụng chính enum làm kiểu biến. Ví dụ:

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

Ngay cả một số câu trả lời cũ đã nhận được 44 phiếu ủng hộ, tôi có xu hướng không đồng ý với tất cả chúng. Tóm lại, tôi không nghĩ chúng ta nên quan tâm đến underlying typeenum.

Trước hết, kiểu Enum của C ++ 03 là một kiểu riêng biệt của nó không có khái niệm về dấu. Vì từ tiêu chuẩn C ++ 03dcl.enum

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

Vì vậy, khi chúng ta đang nói về dấu của một kiểu enum, giả sử khi so sánh 2 toán hạng enum bằng cách sử dụng <toán tử, chúng ta thực sự đang nói về việc chuyển đổi ngầm kiểu enum thành một số kiểu tích phân. Đó là dấu hiệu của loại tích phân này là vấn đề . Và khi chuyển đổi enum thành kiểu tích phân, câu lệnh này áp dụng:

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

Và, rõ ràng, loại enum cơ bản không liên quan gì đến Quảng cáo Tích hợp. Vì tiêu chuẩn xác định Quảng cáo Tích hợp như thế này:

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.

Vì vậy, việc một kiểu enum có trở thành signed inthay không unsigned intphụ thuộc vào việc signed intcó thể chứa tất cả các giá trị của các kiểu liệt kê đã xác định, chứ không phải kiểu cơ bản của enum.

Xem câu hỏi liên quan của tôi Dấu hiệu của C ++ Kiểu Enum Không chính xác Sau khi Chuyển đổi sang Kiểu Tích phân


Nó quan trọng khi bạn đang biên dịch với -Wsign-conversion. Chúng tôi sử dụng nó để giúp bắt những lỗi không mong muốn trong mã của chúng tôi. Nhưng +1 để trích dẫn tiêu chuẩn và chỉ ra rằng một enum không có loại ( signedso với unsigned) nào được liên kết với nó.
jww

4

Trong tương lai, với C ++ 0x, các kiểu liệt kê được đánh mạnh sẽ có sẵn và có một số lợi thế (chẳng hạn như kiểu an toàn, các kiểu cơ bản rõ ràng hoặc phạm vi rõ ràng). Với điều đó, bạn có thể yên tâm hơn về dấu hiệu của loại.


4

Ngoài những gì những người khác đã nói về có dấu / không dấu, đây là những gì tiêu chuẩn nói về phạm vi của kiểu liệt kê:

7.2 (6): "Đối với kiểu liệt kê trong đó e (min) là kiểu nhỏ nhất và e (max) là lớn nhất, các giá trị của kiểu liệt kê là giá trị của kiểu cơ bản trong phạm vi từ b (min) đến b (max ), trong đó b (min) và b (max) lần lượt là các giá trị nhỏ nhất và lớn nhất của trường bit nhỏ nhất có thể lưu trữ e (min) và e (max). Có thể xác định một kiểu liệt kê có các giá trị không được xác định bởi bất kỳ điều tra viên nào của nó. "

Ví dụ:

enum { A = 1, B = 4};

xác định kiểu liệt kê trong đó e (min) là 1 và e (max) là 4. Nếu kiểu cơ bản là int, thì trường bit bắt buộc nhỏ nhất có 4 bit và nếu int trong quá trình triển khai của bạn là phần bù của hai thì phạm vi hợp lệ là enum là -8 đến 7. Nếu kiểu cơ bản là không dấu, thì nó có 3 bit và phạm vi từ 0 đến 7. Hãy kiểm tra tài liệu trình biên dịch của bạn nếu bạn quan tâm (ví dụ: nếu bạn muốn truyền các giá trị tích phân không phải là bảng liệt kê sang kiểu liệt kê, khi đó bạn cần biết giá trị có nằm trong phạm vi của kiểu liệt kê hay không - nếu không giá trị enum kết quả là không xác định).

Việc các giá trị đó có phải là đầu vào hợp lệ cho hàm của bạn hay không có thể là một vấn đề khác với việc chúng có phải là giá trị hợp lệ của kiểu liệt kê hay không. Mã kiểm tra của bạn có thể đang lo lắng về cái trước hơn là cái sau, và vì vậy trong ví dụ này ít nhất nên kiểm tra> = A và <= B.


0

Kiểm tra nó bằng std::is_signed<std::underlying_type+ enums có phạm vi mặc định làint

https://en.cppreference.com/w/cpp/language/enum ngụ ý:

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 ngược dòng .

Biên dịch và chạy:

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

Đầu ra:

0

Đã thử nghiệm trên Ubuntu 16.04, GCC 6.4.0.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.