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)?
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)?
Câu trả lời:
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ý.
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ý).
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
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.
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.
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 type
enum.
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 int
hay không unsigned int
phụ thuộc vào việc signed int
có 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
-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 ( signed
so với unsigned
) nào được liên kết với nó.
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.
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.
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>()));
}
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.