(Làm thế nào) tôi có thể đếm các mục trong một enum?


98

Câu hỏi này nảy ra trong đầu tôi, khi tôi có một cái gì đó như

enum Folders {FA, FB, FC};

và muốn tạo một mảng các vùng chứa cho mỗi thư mục:

ContainerClass*m_containers[3];
....
m_containers[FA] = ...; // etc.

(Sử dụng bản đồ sẽ thanh lịch hơn nhiều khi sử dụng std::map<Folders, ContainerClass*> m_containers;:)

Nhưng để quay lại câu hỏi ban đầu của tôi: Điều gì xảy ra nếu tôi không muốn mã hóa kích thước mảng, có cách nào để tìm ra có bao nhiêu mục trong Thư mục không? (Không dựa vào ví dụ như FClà mục cuối cùng trong danh sách sẽ cho phép một cái gì đó như ContainerClass*m_containers[FC+1]nếu tôi không nhầm.)


Bài đăng này có thể trả lời câu hỏi của bạn: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked

1
Câu hỏi là một chút không xác định. Theo tiêu chuẩn C ++, int(FA) | int(FB) | int (FC)cũng là một giá trị pháp lý cho một Foldersbiến. Nếu bạn định kích thước m_containersđể bất kỳ Foldersbiến nào là một chỉ mục hợp lệ, [FC+1]sẽ không đủ lớn.
MSalters

Tôi đã hỏi một điều rất liên quan trên stackoverflow.com/questions/12972317/count-on-enum-c-automatic
sergiol

Tôi khuyên bạn giải pháp từ stackoverflow.com/a/60216003/12894563 và biến thể cải tiến từ stackoverflow.com/a/60271408/12894563
ixjxk

Câu trả lời:


123

Thực sự không có cách nào tốt để làm điều này, thông thường bạn sẽ thấy một mục bổ sung trong enum, tức là

enum foobar {foo, bar, baz, quz, FOOBAR_NR_ITEMS};

Vì vậy, sau đó bạn có thể làm:

int fuz[FOOBAR_NR_ITEMS];

Tuy nhiên, vẫn không tốt lắm.

Nhưng tất nhiên bạn có nhận ra rằng chỉ số mục trong một enum là không an toàn, ví dụ:

enum foobar {foo, bar = 5, baz, quz = 20};

số mục sẽ là 4, nhưng giá trị nguyên của các giá trị enum sẽ nằm ngoài phạm vi chỉ số mảng. Sử dụng các giá trị enum để lập chỉ mục mảng là không an toàn, bạn nên xem xét các tùy chọn khác.

chỉnh sửa: theo yêu cầu, làm cho mục đặc biệt nhô ra nhiều hơn.


27
Gọi là LAST hoặc ALWAYS_AT_END hoặc điều gì đó không quá khó hiểu. Làm cho nó nhô ra . Để những người bảo trì tiếp theo không vô tình thêm các mục mới sau khi bạn kết thúc điểm đánh dấu.
Martin York

3
Dù tốt hơn hay tệ hơn, đây là cách tiếp cận mà chúng tôi thực hiện trong tổ chức của mình. Chúng tôi thường gọi nó là FINAL_enumname_ENTRY, chẳng hạn như FINAL_foobar_ENTRY. Tôi cũng đã thấy mọi người sử dụng biến const FOOBAR_COUNT tĩnh riêng biệt được định nghĩa ngay sau khai báo enum, một cách tiếp cận dễ bị lỗi hơn một chút.
Darryl

1
Ít nhất thì khá dễ dàng để thấy rằng "enum foo {a = 10, LAST}" sẽ là một điều kỳ lạ. Và tôi nghĩ "int arr [LAST]" sẽ là 11 mặt hàng trong trường hợp này, không phải 2, vì vậy hầu hết các mã sẽ làm việc (nhưng bạn nhớ đang lãng phí trên các giá trị chỉ số không hợp lệ)
Mã Abominator

32

Đối với C ++, có nhiều kỹ thuật enum an toàn kiểu khác nhau và một số kỹ thuật (chẳng hạn như Boost.Enum được đề xuất nhưng chưa bao giờ được gửi ) bao gồm hỗ trợ để lấy kích thước của một enum.

Cách tiếp cận đơn giản nhất, hoạt động trong C cũng như C ++, là áp dụng quy ước khai báo giá trị ... MAX cho mỗi kiểu enum của bạn:

enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

Chỉnh sửa : Về { FA, FB, FC, Folders_MAX = FC}so với {FA, FB, FC, Folders_MAX]: Tôi thích đặt giá trị ... MAX thành giá trị pháp lý cuối cùng của enum vì một vài lý do:

  1. Tên của hằng số chính xác hơn về mặt kỹ thuật (vì Folders_MAXcho giá trị enum lớn nhất có thể).
  2. Cá nhân tôi cảm thấy mình Folders_MAX = FCnổi bật hơn các mục nhập khác một chút (khiến việc vô tình thêm giá trị enum mà không cập nhật giá trị tối đa khó hơn một chút, một vấn đề mà Martin York đã tham khảo).
  3. GCC bao gồm các cảnh báo hữu ích như "giá trị liệt kê không được bao gồm trong công tắc" cho mã như sau. Việc để Folders_MAX == FC + 1 phá vỡ những cảnh báo đó, vì bạn kết thúc với một loạt ... MAX giá trị liệt kê không bao giờ được đưa vào công tắc.
chuyển đổi (thư mục) 
{
  trường hợp FA: ...;
  vụ FB: ...;
  // Rất tiếc, quên FC!
}

3
Tại sao không làm enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];:?
Hóa đơn

1
Tôi thích để làm cho rõ ràng rằng cuối cùng là một con số, và tất cả họ đều có cùng tên nhờ:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte

2
Trên thực tế, tôi cảm thấy những cảnh báo "hữu ích" là một nỗi đau đúng trong ass. Tôi thích những cảnh báo tốt mà tôi luôn đặt -Wall -pedantic, v.v. khi tôi đang phát triển, nhưng những cảnh báo này thật ngu ngốc. Chỉ có một số điều tệ hơn, như gợi ý parens cho && || và & ^ | ưu tiên điều hành. Ý tôi là, tôi đã nghĩ java là ngôn ngữ giữ trẻ, cái quái gì đang xảy ra với C và C ++ ...
wich

2
Nhược điểm của việc có Folders_max = FC là bạn phải thay đổi nó mỗi khi bạn thêm thứ gì đó vào enum!
Étienne

2
enum Thư mục {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Chỉ để nhấn mạnh rằng nó hữu ích cho việc lặp lại?
gjpc

7

Còn về các đặc điểm, theo kiểu STL? Ví dụ:

enum Foo
{
    Bar,
    Baz
};

viết một

std::numeric_limits<enum Foo>::max()

chuyên môn hóa (có thể là constexpr nếu bạn sử dụng c ++ 11). Sau đó, trong mã thử nghiệm của bạn, hãy cung cấp bất kỳ xác nhận tĩnh nào để duy trì các ràng buộc std :: numeric_limits :: max () = last_item.


2
Thật không may, điều này sẽ không hoạt động theo câu trả lời này .
rr-

3
std::numeric_limits<enum Foo>::max()luôn trả về số không ... (xem câu hỏi để biết câu trả lời được liên kết) Đã thử nghiệm trên enums bình thường ( enum Foo { ... }), enums kiểu gợi ý ( enum class Foo : uint8_t { ... }) với gcc 5.2.0 @ Linux và MinGW 4.9.3 @ Windows.
rr-

1
(... và trong trường hợp của std::numeric_limits<std::underlying_type<Foo>::type>::max(), nó trả về giá trị tối đa của kiểu cơ bản tức là 0xFFFFFFFF cho số nguyên 32 bit, điều này không hữu ích trong ngữ cảnh này.)
rr-

1
Kỳ lạ, bởi vì tôi có mã làm việc mà tôi đã mô tả. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Sau đó: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;in kết quả mong đợi. Đã thử nghiệm với hương vị gcc 5.2.1 và 4.8 / 4.9.
Wojciech Migda

2
-1; ping cho tôi nếu bạn thay đổi câu trả lời, vì vậy tôi có thể hoàn tác phản đối. Câu trả lời này là một quy ước tồi để làm theo. Đó là một cách sử dụng sai khái niệm numeric_limits<T>::max(). Điều duy nhất mà hàm có thể trả về một cách hợp lý là giá trị được liệt kê cao nhất. Nó sẽ quay trở lại 2, nhưng OP (trong trường hợp cụ thể này) sẽ cần nó quay trở lại 3. Một khi bạn có các giá trị không mặc định cho enum ( FB = 2057), tất cả các cược sẽ bị tắt, thậm chí không + 1thể hack xung quanh lỗi từng người một. Nếu có numeric_limits<T>::number_of_elements_of_the_set()(hoặc tên ngắn hơn), bạn có thể sử dụng tên đó mà không bị mơ hồ.
Merlyn Morgan-Graham

3

Thêm một mục, ở cuối enum của bạn, được gọi là Folders_MAX hoặc một cái gì đó tương tự và sử dụng giá trị này khi khởi tạo mảng của bạn.

ContainerClass* m_containers[Folders_MAX];

2

Tôi thích sử dụng enum làm đối số cho các hàm của mình. Đó là một phương tiện dễ dàng để cung cấp một danh sách "tùy chọn" cố định. Vấn đề với câu trả lời được bình chọn nhiều nhất ở đây là khi sử dụng câu trả lời đó, khách hàng có thể chỉ định một "tùy chọn không hợp lệ". Về cơ bản, tôi khuyên bạn nên làm điều tương tự về cơ bản, nhưng sử dụng một hằng số int bên ngoài enum để xác định số lượng của chúng.

enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

Nó không dễ chịu, nhưng đó là một giải pháp sạch nếu bạn không thay đổi enum mà không cập nhật hằng số.


1

Tôi thực sự không thấy bất kỳ cách nào để thực sự nhận được số lượng giá trị trong một kiểu liệt kê trong C ++. Bất kỳ giải pháp nào trong số các giải pháp đề cập trước đều hoạt động miễn là bạn không xác định giá trị của các bảng liệt kê của mình nếu bạn xác định bạn đánh giá rằng bạn có thể gặp phải các tình huống trong đó bạn tạo mảng quá lớn hoặc quá nhỏ

enum example{ test1 = -2, test2 = -1, test3 = 0, test4 = 1, test5 = 2 }

trong phần này về các ví dụ, kết quả sẽ tạo ra một mảng gồm 3 mục khi bạn cần một mảng gồm 5 mục

enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

trong phần này về các ví dụ, kết quả sẽ tạo ra một mảng 301 mục khi bạn cần một mảng gồm 5 mục

Cách tốt nhất để giải quyết vấn đề này trong trường hợp chung là lặp lại các bảng liệt kê của bạn nhưng điều đó vẫn chưa có trong tiêu chuẩn theo như tôi biết

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.