Màu sắc được đặt theo tiêu chuẩn là gì?
Trả lời bằng một trích dẫn từ Tiêu chuẩn C ++ 11 và C ++ 14:
[expr.static.cast] / 10
Giá trị của loại tích phân hoặc liệt kê có thể được chuyển đổi rõ ràng thành loại liệt kê. Giá trị không thay đổi nếu giá trị ban đầu nằm trong phạm vi của các giá trị liệt kê (7.2). Mặt khác, giá trị kết quả là không xác định (và có thể không nằm trong phạm vi đó).
Hãy tìm kiếm phạm vi của các giá trị liệt kê : [dcl.enum] / 7
Đối với một phép liệt kê có kiểu cơ bản được cố định, các giá trị của phép liệt kê là các giá trị của kiểu bên dưới.
Trước CWG 1766 (C ++ 11, C ++ 14)
Do đó, đối với data[0] == 100
, giá trị kết quả được chỉ định (*) và không có Hành vi không xác định (UB) nào được tham gia. Tổng quát hơn, khi bạn chuyển từ loại cơ bản sang loại liệt kê, không có giá trị nào data[0]
có thể dẫn đến UB cho static_cast
.
Sau CWG 1766 (C ++ 17)
Xem khuyết tật CWG 1766 . Đoạn p10 [expr.static.cast] đã được tăng cường, vì vậy bây giờ bạn có thể gọi UB nếu bạn đặt một giá trị nằm ngoài phạm vi có thể biểu thị của một enum cho loại enum. Điều này vẫn không áp dụng cho kịch bản trong câu hỏi, vì data[0]
thuộc loại liệt kê cơ bản (xem ở trên).
Xin lưu ý rằng CWG 1766 được coi là một khiếm khuyết trong Tiêu chuẩn, do đó, nó được chấp nhận cho những người triển khai trình biên dịch áp dụng cho các chế độ biên dịch C ++ 11 và C ++ 14 của họ.
(*) char
được yêu cầu rộng tối thiểu 8 bit, nhưng không bắt buộc phải có unsigned
. Giá trị lưu trữ tối đa được yêu cầu tối thiểu phải 127
theo Phụ lục E của Tiêu chuẩn C99.
So sánh với [expr] / 4
Nếu trong quá trình đánh giá biểu thức, kết quả không được xác định theo toán học hoặc không nằm trong phạm vi giá trị đại diện cho loại của nó, hành vi không được xác định.
Trước CWG 1766, loại tích phân chuyển đổi -> loại liệt kê có thể tạo ra một giá trị không xác định . Câu hỏi là: một giá trị không xác định có thể nằm ngoài các giá trị đại diện cho loại của nó không? Tôi tin rằng câu trả lời là không - nếu câu trả lời là có , sẽ không có sự khác biệt nào trong các đảm bảo bạn nhận được cho các thao tác trên các loại đã ký giữa "thao tác này tạo ra giá trị không xác định" và "thao tác này có hành vi không xác định".
Do đó, trước CWG 1766, thậm chí static_cast<Color>(10000)
sẽ không gọi UB; nhưng sau khi CWG 1766, nó không invoke UB.
Bây giờ, switch
tuyên bố:
[stmt.switch] / 2
Điều kiện phải là loại tích phân, loại liệt kê hoặc loại lớp. [...] Các chương trình khuyến mãi tích hợp được thực hiện.
[conv.prom] / 4
Một prvalue của một unscoped kiểu enumeration có tiềm ẩn loại là cố định (7.2) có thể được chuyển đổi sang một prvalue loại cơ bản của nó. Ngoài ra, nếu quảng cáo tích hợp có thể được áp dụng cho loại cơ sở của nó, thì giá trị của loại liệt kê không có phạm vi có loại cơ bản được cố định cũng có thể được chuyển đổi thành giá trị của loại cơ sở được quảng cáo.
Lưu ý: Loại cơ bản của enum có phạm vi w / o enum-base là int
. Đối với các enum không có phạm vi, kiểu bên dưới được xác định theo thực thi, nhưng không được lớn hơn int
nếu int
có thể chứa các giá trị của tất cả các điều tra viên.
Đối với một phép liệt kê không giới hạn , điều này dẫn chúng ta đến / 1
Một prvalue của một kiểu số nguyên khác hơn bool
, char16_t
, char32_t
, hoặc wchar_t
có số nguyên chuyển đổi cấp bậc (4.13) là thấp hơn cấp bậc int
có thể được chuyển đổi sang một prvalue loại int
nếu int
có thể đại diện cho tất cả các giá trị của các loại nguồn; mặt khác, giá trị nguồn có thể được chuyển đổi thành giá trị loại unsigned int
.
Trong trường hợp của một unscoped điều tra, chúng tôi sẽ đối phó với int
s đây. Đối với bảng liệt kê phạm vi ( enum class
và enum struct
), không áp dụng khuyến mãi tích hợp. Trong bất kỳ cách nào, quảng cáo tích hợp cũng không dẫn đến UB, vì giá trị được lưu trữ nằm trong phạm vi của loại cơ bản và trong phạm vi int
.
[stmt.switch] / 5
Khi switch
câu lệnh được thực thi, điều kiện của nó được ước tính và so sánh với từng hằng số trường hợp. Nếu một trong các hằng số trường hợp bằng giá trị của điều kiện, điều khiển được chuyển đến câu lệnh theo case
nhãn phù hợp . Nếu không có case
hằng số phù hợp với điều kiện và nếu có default
nhãn, điều khiển sẽ chuyển đến câu lệnh được gắn nhãn bởi default
nhãn.
Các default
nhãn nên được nhấn.
Lưu ý: Người ta có thể có cái nhìn khác về toán tử so sánh, nhưng nó không được sử dụng rõ ràng trong "so sánh" được đề cập. Trong thực tế, không có gợi ý nào về việc giới thiệu UB cho các enum có phạm vi hoặc không có phạm vi trong trường hợp của chúng tôi.
Là một phần thưởng, tiêu chuẩn có đảm bảo bất kỳ điều gì về điều này nhưng với enum đơn giản?
Cho dù có hay không enum
phạm vi không làm cho bất kỳ sự khác biệt ở đây. Tuy nhiên, nó sẽ tạo ra sự khác biệt cho dù loại cơ bản có được sửa hay không. Toàn bộ [Dec.enum] / 7 là:
Đối với một phép liệt kê có kiểu cơ bản được cố định, các giá trị của phép liệt kê là các giá trị của kiểu bên dưới. Nếu không, cho một điều tra nơi e phút là các điều tra viên và nhỏ e max là lớn nhất, các giá trị thuộc kiểu liệt kê là các giá trị trong phạm vi b phút để b max , được xác định như sau: Hãy K
thể 1
cho đại diện bổ sung của hai và 0
cho một bổ sung hoặc đại diện dấu hiệu của một. b max là giá trị nhỏ nhất lớn hơn hoặc bằng max (| e min | - K
, | e max |) và bằng 2M - 1 , trong đóM
một số nguyên không âm. b min bằng 0 nếu e min không âm và - (b max + K
) nếu không.
Chúng ta hãy xem bảng liệt kê sau:
enum ColorUnfixed /* no fixed underlying type */
{
red = 0x1,
yellow = 0x2
}
Lưu ý rằng chúng ta không thể định nghĩa đây là một enum có phạm vi, vì tất cả các enum có phạm vi đều có các kiểu cơ bản cố định.
May mắn thay, ColorUnfixed
liệt kê nhỏ nhất là red = 0x1
, vì vậy max (| e min | - K
, | e max |) bằng với | e max | trong mọi trường hợp, đó là yellow = 0x2
. Giá trị nhỏ nhất lớn hơn hoặc bằng 2
, bằng 2 M - 1 cho số nguyên dương M
là 3
( 2 2 - 1 ). (Tôi nghĩ rằng mục đích là cho phép phạm vi mở rộng trong các bước 1 bit.) Theo sau đó b max là 3
và bmin là 0
.
Do đó, 100
sẽ nằm ngoài phạm vi ColorUnfixed
và static_cast
sẽ tạo ra một giá trị không xác định trước CWG 1766 và hành vi không xác định sau CWG 1766.
char
, vì vậy "Giá trị không thay đổi nếu giá trị ban đầu nằm trong phạm vi của các giá trị liệt kê (7.2)." áp dụng.