我该如何计算枚举中的项数?

112

我脑海中浮现出这个问题,是因为我遇到了类似的情况

enum Folders {FA, FB, FC};

并希望创建一个容器数组以存储每个文件夹:

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

使用映射表更加优雅,代码如下:std::map<Folders, ContainerClass*> m_containers;

但回到我的最初问题:如果我不想硬编码数组大小,有没有一种方法可以找出Folders中有多少项?(不能依赖于例如FC是列表中的最后一项,这将允许类似于ContainerClass*m_containers[FC+1]的操作,如果我没记错的话。)


这篇文章可能会回答你的问题:https://dev59.com/eXM_5IYBdhLWcg3waSX9。 - StackedCrooked
1
问题有一点未明确。根据C++标准,int(FA) | int(FB) | int (FC) 也是 一个 Folders 变量的合法值。如果你调整 m_containers 的大小,以使任何 Folders 变量都是有效索引,则 [FC+1] 将不够大。 - MSalters
我在https://dev59.com/-mnWa4cB1Zd3GeqP4M2y上询问了一个非常相关的问题。 - sergiol
我建议您使用 https://dev59.com/YWUp5IYBdhLWcg3w3aZ1#60216003 中的解决方案以及 https://dev59.com/-mnWa4cB1Zd3GeqP4M2y#60271408 的改进版本。 - ixjxk
7个回答

139

实际上没有一个很好的方法来做到这一点,通常你会在枚举中看到一个额外的项,例如:

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

那么你可以这样做:

int fuz[FOOBAR_NR_ITEMS];

不过还是有点糟糕。

但是你当然意识到,仅仅枚举中项目的数量并不安全,例如:

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

项目数量将为4,但枚举值的整数值将远超过数组索引范围。使用枚举值进行数组索引不安全,您应考虑其他选项。

编辑:根据要求,使特殊条目更加突出。


31
将其称为LAST或ALWAYS_AT_END或其他不那么难懂的名称。让它__突出显示__。这样,后续的维护人员就不会在结束标记之后意外添加新条目。 - Martin York
3
无论好坏,这是我们组织采取的方法。通常我们称其为FINAL_enumname_ENTRY,例如FINAL_foobar_ENTRY。我也看到过有人在枚举声明后使用单独的静态常量FOOBAR_COUNT变量的方法,这种方法略微容易出现错误。 - Darryl
1
至少很容易看出 "enum foo {a=10,LAST}" 会是奇数。而我认为 "int arr[LAST]" 在这种情况下应该是11项,而不是2项,所以大多数代码都能正常工作(但你浪费了内存在无效的索引值上)。 - Code Abominator

36
对于C++,有各种类型安全的枚举技术可用,其中一些(例如拟议但从未提交的Boost.Enum)包括支持获取枚举大小。
最简单的方法是采用一种约定,为每个枚举类型声明一个...MAX值,这适用于C和C++。
enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

编辑: 关于{ FA, FB, FC, Folders_MAX = FC}{FA, FB, FC, Folders_MAX]的区别:我更喜欢将...MAX值设置为枚举的最后一个合法值,原因如下:

  1. 常量的名称在技术上更准确(因为Folders_MAX给出了可能的最大枚举值)。
  2. 个人认为Folders_MAX = FC与其他条目有所不同(使它更难无意中添加枚举值而不更新最大值,这是Martin York提到的问题)。
  3. GCC包括有用的警告,例如“未包含在switch中的枚举值”的代码。让Folders_MAX == FC + 1会破坏这些警告,因为你最终会得到一堆...MAX枚举值,它们永远不应该被包含在switch中。
switch (folder) 
{
  case FA: ...;
  case FB: ...;
  // 哎呀,忘记FC了!
}

3
为什么不这样做:enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX]; - Bill
1
我更喜欢通过以下方式明确最后一个是数字,并且它们都有相同的名称:struct SomeEnum { enum type {FA, FB, FC, NB__};}; - Luc Hermitte
2
其实我觉得那些“有用”的警告真的很烦人。我喜欢好的警告,我总是在开发时设置-Wall -pedantic等,但这些警告只是愚蠢的。只有少数更糟糕,比如建议使用括号来表示&& ||和& ^ |运算符优先级。我的意思是,我以为Java是保姆语言,C和C++怎么了... - wich
2
Folders_max=FC 的缺点是每次添加枚举时都必须更改它! - Étienne
2
枚举文件夹 { FA,FB,FC,Folders_MIN = FA,Folders_MAX = FC }; 只是强调它对迭代很有用? - gjpc
显示剩余3条评论

10

那么将特征以STL的方式呈现呢?例如:

enum Foo
{
    Bar,
    Baz
};
写一个
std::numeric_limits<enum Foo>::max()

在你的代码中,使用专业化(如果你使用C++11,则可能是constexpr)。然后,在你的测试代码中提供任何静态断言以保持std::numeric_limits::max() = last_item的约束。


2
很遗憾,根据这个答案,这将不起作用。 - rr-
3
std::numeric_limits<enum Foo>::max() 始终返回零...(请参见链接答案的问题) 在 Linux 上测试了常规枚举 (enum Foo { ... })、类型提示枚举 (enum class Foo : uint8_t { ... }),以及在 Windows 上使用 gcc 5.2.0 和 MinGW 4.9.3 进行测试。 - rr-
1
(...在std::numeric_limits<std::underlying_type<Foo>::type>::max()的情况下,它返回基础类型的最大值,即32位整数的0xFFFFFFFF,在这种情况下并不有用。) - rr-
1
@rr- 你确定针对这个枚举类型的::max()特化仍然会失败吗?我读到你的评论好像你错过了这个答案中的“特化”。 - moala
2
-1;如果您更改答案,请通知我,以便我可以取消踩。这个答案是一个不好的惯例。这是对numeric_limits<T>::max()概念的误用。该函数唯一合理的返回值是最高枚举值。它将返回2,但在这种特定情况下,OP需要它返回3。一旦枚举类型有了非默认值(例如FB = 2057),所有的赌注都是无效的,甚至不能通过+1来解决偏移错误。如果有一个numeric_limits<T>::number_of_elements_of_the_set()(或者更短的名称),那么就可以无歧义地使用它。 - Merlyn Morgan-Graham
显示剩余2条评论

3
在你的枚举末尾添加一个名为“Folders_MAX”的条目或类似的内容,并在初始化数组时使用此值。
ContainerClass* m_containers[Folders_MAX];

2

我真的看不出如何在C++中获取枚举中值的数量。只要您不定义枚举的值,任何先前提到的解决方案都可以正常工作,但如果您定义了值,您可能会遇到创建过大或过小的数组的情况。

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

在这些示例中,结果会创建一个包含3个项目的数组,而当你需要一个包含5个项目的数组时则不够用。
enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

在这个例子中,结果会创建一个包含301个项目的数组,而您需要的是一个包含5个项目的数组。
在一般情况下解决此问题的最佳方法是遍历枚举,但据我所知这还不在标准中。

2
我喜欢将枚举用作函数的参数。这是提供“选项”固定列表的简单方法。这里最受欢迎的答案的问题在于,使用它,客户端可以指定“无效选项”。作为一个衍生,我建议基本上做同样的事情,但是使用枚举外部的常量int来定义它们的计数。
enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

这不是很愉快,但如果你不改变枚举而更新常量,这是一个干净的解决方案。

1

这是在编译时执行的最佳方法。我使用了这里中的arg_var计数答案。

#define PP_NARG(...) \
     PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)

#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

#define TypedEnum(Name, ...)                                      \
struct Name {                                                     \
    enum {                                                        \
        __VA_ARGS__                                               \
    };                                                            \
    static const uint32_t Name##_MAX = PP_NARG(__VA_ARGS__);      \
}

#define Enum(Name, ...) TypedEnum(Name, __VA_ARGS__)

声明枚举:

Enum(TestEnum, 
Enum_1= 0,
Enum_2= 1,
Enum_3= 2,
Enum_4= 4,
Enum_5= 8,
Enum_6= 16,
Enum_7= 32);

最大值将在此处提供:

int array [TestEnum::TestEnum_MAX];
for(uint32_t fIdx = 0; fIdx < TestEnum::TestEnum_MAX; fIdx++)
{
     array [fIdx] = 0;
}

2
我不会称其为“C++”。 - avp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接