C ++ enums ลงนามหรือไม่ได้ลงนาม? และโดยส่วนขยายจะปลอดภัยหรือไม่ที่จะตรวจสอบอินพุตโดยตรวจสอบว่าเป็น <= ค่าสูงสุดของคุณและเว้นไว้> = ค่าขั้นต่ำของคุณ (สมมติว่าคุณเริ่มต้นที่ 0 และเพิ่มขึ้นทีละ 1)
C ++ enums ลงนามหรือไม่ได้ลงนาม? และโดยส่วนขยายจะปลอดภัยหรือไม่ที่จะตรวจสอบอินพุตโดยตรวจสอบว่าเป็น <= ค่าสูงสุดของคุณและเว้นไว้> = ค่าขั้นต่ำของคุณ (สมมติว่าคุณเริ่มต้นที่ 0 และเพิ่มขึ้นทีละ 1)
คำตอบ:
คุณไม่ควรพึ่งพาการแสดงเฉพาะใด ๆ อ่านต่อไปนี้การเชื่อมโยง นอกจากนี้มาตรฐานยังระบุว่ามีการกำหนดการนำไปใช้งานซึ่งประเภทอินทิกรัลถูกใช้เป็นประเภทพื้นฐานสำหรับ enum ยกเว้นว่าจะต้องไม่ใหญ่กว่า int เว้นแต่ค่าบางค่าจะไม่สามารถใส่ลงใน int หรือ int ที่ไม่ได้ลงนามได้
ในระยะสั้น: คุณไม่สามารถพึ่งพา enum ที่ลงชื่อหรือไม่ได้ลงนาม
ไปที่มากันเถอะ นี่คือสิ่งที่เอกสารมาตรฐาน C ++ 03 (ISO / IEC 14882: 2003) ระบุไว้ใน 7.2-5 (การประกาศการแจงนับ):
ชนิดพื้นฐานของการแจงนับเป็นชนิดอินทิกรัลที่สามารถแทนค่าตัวนับทั้งหมดที่กำหนดไว้ในการแจงนับ มีการกำหนดการนำไปใช้งานซึ่งชนิดอินทิกรัลถูกใช้เป็นประเภทพื้นฐานสำหรับการแจงนับยกเว้นว่าประเภทพื้นฐานจะต้องไม่ใหญ่กว่า int เว้นแต่ค่าของตัวแจงนับไม่สามารถใส่ใน int หรือ int ที่ไม่ได้ลงนาม
ในระยะสั้นคอมไพเลอร์ของคุณมีให้เลือก (เห็นได้ชัดว่าหากคุณมีตัวเลขติดลบสำหรับค่าการแจงนับบางค่ามันจะถูกเซ็นชื่อ)
คุณไม่ควรขึ้นอยู่กับว่าพวกเขาลงนามหรือไม่ได้ลงนาม หากคุณต้องการให้พวกเขาลงนามอย่างชัดเจนหรือไม่ได้ลงนามคุณสามารถใช้สิ่งต่อไปนี้:
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
คุณไม่ควรพึ่งพาการลงนามหรือไม่ได้ลงนาม ตามมาตรฐานมีการกำหนดให้ใช้งานได้ว่าประเภทอินทิกรัลถูกใช้เป็นประเภทพื้นฐานสำหรับ enum แม้ว่าในการใช้งานส่วนใหญ่จะเป็นจำนวนเต็มที่มีการลงนาม
ใน C ++ 0x การแจงนับที่พิมพ์อย่างรุนแรงจะถูกเพิ่มซึ่งจะช่วยให้คุณสามารถระบุประเภทของ enum เช่น:
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
แม้ว่าในตอนนี้การตรวจสอบความถูกต้องอย่างง่ายบางอย่างสามารถทำได้โดยใช้ 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.
คอมไพเลอร์สามารถตัดสินใจได้ว่าจะเซ็นชื่อหรือไม่ลงนาม enums
อีกวิธีหนึ่งในการตรวจสอบ enums คือการใช้ enum เป็นตัวแปรชนิด ตัวอย่างเช่น:
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.
แม้แต่คำตอบเก่า ๆ บางคำก็ได้รับการโหวตถึง 44 ครั้งฉันก็มักจะไม่เห็นด้วยกับคำตอบทั้งหมด ในระยะสั้นฉันไม่คิดว่าเราควรสนใจเกี่ยวกับunderlying type
enum
ก่อนอื่นประเภท C ++ 03 Enum เป็นประเภทที่แตกต่างกันโดยไม่มีแนวคิดในการลงนาม ตั้งแต่มาตรฐาน C ++ 03dcl.enum
7.2 Enumeration declarations
5 Each enumeration defines a type that is different from all other types....
ดังนั้นเมื่อเรากำลังพูดถึงสัญลักษณ์ของชนิด enum ให้พูดว่าเมื่อเปรียบเทียบตัวถูกดำเนินการ 2 enum โดยใช้ตัวดำเนิน<
การเรากำลังพูดถึงการแปลงประเภท enum เป็นอินทิกรัลบางประเภทโดยปริยาย มันเป็นสัญลักษณ์ของประเภทหนึ่งที่เรื่อง และเมื่อแปลง enum เป็นชนิดอินทิกรัลคำสั่งนี้ใช้:
9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).
และเห็นได้ชัดว่าประเภทพื้นฐานของ enum ไม่มีส่วนเกี่ยวข้องกับ Integral Promotion เนื่องจากมาตรฐานกำหนด 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.
ดังนั้นไม่ว่าประเภท enum จะกลายเป็นsigned int
หรือไม่unsigned int
ขึ้นอยู่กับว่าsigned int
สามารถมีค่าทั้งหมดของตัวนับที่กำหนดไว้ได้หรือไม่ไม่ใช่ประเภทพื้นฐานของ enum
ดูคำถามที่เกี่ยวข้องของฉัน เครื่องหมายของ C ++ Enum Type ไม่ถูกต้องหลังจากแปลงเป็น Integral Type
-Wsign-conversion
มันเป็นเรื่องสำคัญเมื่อคุณกำลังรวบรวมกับ เราใช้เพื่อช่วยจับข้อผิดพลาดที่ไม่ได้ตั้งใจในโค้ดของเรา แต่+1สำหรับการอ้างถึงมาตรฐานและชี้ให้เห็นว่า enum ไม่มีประเภท ( signed
เทียบกับunsigned
) ที่เกี่ยวข้อง
ในอนาคตด้วย C ++ 0x การแจงนับที่พิมพ์มากจะพร้อมใช้งานและมีข้อดีหลายประการ (เช่นประเภทความปลอดภัยประเภทพื้นฐานที่ชัดเจนหรือขอบเขตที่ชัดเจน) ด้วยเหตุนี้คุณจึงสามารถมั่นใจได้ดีขึ้นถึงสัญลักษณ์ของประเภท
นอกเหนือจากสิ่งที่คนอื่นพูดเกี่ยวกับการลงนาม / ไม่ได้ลงนามแล้วนี่คือสิ่งที่มาตรฐานกล่าวเกี่ยวกับช่วงของประเภทที่แจกแจง:
7.2 (6): "สำหรับการแจงนับโดยที่ e (นาที) เป็นตัวแจงนับที่เล็กที่สุดและ e (สูงสุด) มีค่ามากที่สุดค่าของการแจงนับคือค่าของประเภทที่อยู่ในช่วง b (นาที) ถึง b (สูงสุด ) โดยที่ b (min) และ b (max) เป็นค่าที่เล็กที่สุดและมากที่สุดของ bitfield ที่เล็กที่สุดที่สามารถจัดเก็บ e (min) และ e (max) ได้เป็นไปได้ที่จะกำหนดการแจงนับที่ไม่ได้กำหนดค่าไว้ โดยผู้แจงนับ "
ตัวอย่างเช่น:
enum { A = 1, B = 4};
กำหนดประเภทการแจกแจงโดยที่ e (min) คือ 1 และ e (max) คือ 4 หากประเภทที่อยู่ภายใต้ลงนาม int แล้ว bitfield ที่เล็กที่สุดที่ต้องการจะมี 4 บิตและหาก ints ในการนำไปใช้ของคุณเป็นส่วนเสริมสองค่าช่วงที่ถูกต้องของ enum คือ -8 ถึง 7 หากไม่ได้ลงนามประเภทพื้นฐานจะมี 3 บิตและช่วงคือ 0 ถึง 7 ตรวจสอบเอกสารคอมไพเลอร์ของคุณว่าคุณสนใจ (ตัวอย่างเช่นหากคุณต้องการส่งค่าอินทิกรัลอื่นที่ไม่ใช่ตัวนับไปยัง ประเภทที่แจกแจงแล้วคุณต้องรู้ว่าค่าอยู่ในช่วงของการแจงนับหรือไม่ - ถ้าไม่ใช่ค่า enum ที่เป็นผลลัพธ์จะไม่ระบุ)
ค่าเหล่านั้นเป็นอินพุตที่ถูกต้องสำหรับฟังก์ชันของคุณหรือไม่อาจเป็นปัญหาที่แตกต่างจากค่าที่ระบุเป็นค่าที่ถูกต้องหรือไม่ รหัสการตรวจสอบของคุณน่าจะกังวลเกี่ยวกับอดีตมากกว่าอย่างหลังดังนั้นในตัวอย่างนี้อย่างน้อยควรตรวจสอบ> = A และ <= B
ตรวจสอบด้วยstd::is_signed<std::underlying_type
+ ขอบเขต enums เริ่มต้นเป็น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>()));
}
รวบรวมและเรียกใช้:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main
เอาท์พุต:
0
ทดสอบบน Ubuntu 16.04, GCC 6.4.0