我只是注意到不能在枚举上使用标准数学运算符,如++或+=。

那么,在c++枚举中遍历所有值的最佳方法是什么?


当前回答

enum class A {
    a0=0, a3=3, a4=4
};
constexpr std::array<A, 3> ALL_A {A::a0, A::a3, A::a4}; // constexpr is important here

for(A a: ALL_A) {
  if(a==A::a0 || a==A::a4) std::cout << static_cast<int>(a);
}

constexpr std::array甚至可以迭代非顺序的枚举,而无需编译器实例化数组。这取决于编译器的优化启发式以及是否取数组的地址。

In my experiments, I found that g++ 9.1 with -O3 will optimize away the above array if there are 2 non-sequential values or quite a few sequential values (I tested up to 6). But it only does this if you have an if statement. (I tried a statement that compared an integer value greater than all the elements in a sequential array and it inlined the iteration despite none being excluded, but when I left out the if statement, the values were put in memory.) It also inlined 5 values from a non-sequential enum in [one case|https://godbolt.org/z/XuGtoc]. I suspect this odd behavior is due to deep heuristics having to do with caches and branch prediction.

这里有一个godbolt的简单测试迭代的链接,演示了数组并不总是被实例化。

这种技术的代价是写入enum元素两次,并保持两个列表同步。

其他回答

这些解决方案太复杂了,我喜欢这样:

enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};

const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };

for (NodePosition pos : NodePositionVector) {
...
}

如果枚举以0开头,且增量始终为1。

enum enumType 
{ 
    A = 0,
    B,
    C,
    enumTypeEnd
};

for(int i=0; i<enumTypeEnd; i++)
{
   enumType eCurrent = (enumType) i;            
}

如果没有,我想唯一的原因是创造一个像

vector<enumType> vEnums;

添加条目,并使用普通的迭代器....

扩展@Eponymous的回答:它很棒,但没有提供通用语法。这是我想到的:

// Common/EnumTools.h
#pragma once

#include <array>

namespace Common {

// Here we forward-declare metafunction for mapping enums to their values.
// Since C++<23 doesn't have reflection, you have to populate it yourself :-(
// Usage: After declaring enum class E, add this overload in the namespace of E:
// inline constexpr auto allValuesArray(const E&, Commob::EnumAllValuesTag) { return std::array{E::foo, E::bar}; }
// Then `AllValues<NS::E>` will call `allValuesArray(NS::E{}, EnumAllValuesTag)` which will resolve
// by ADL.
// Just be sure to keep it sync'd with your enum!

// Here's what you want to use in, e.g., loops: "for (auto val : Common::AllValues<MyEnum>) {"

struct EnumAllValuesTag {}; // So your allValuesArray function is clearly associated with this header.

template <typename Enum>
static inline constexpr auto AllValues = allValuesArray(Enum{}, EnumAllValuesTag{});
// ^ Just "constexpr auto" or "constexpr std::array<Enum, allValuesArray(Enum{}, EnumAllValuesTag{}).size()>" didn't work on all compilers I'm using, but this did.

} // namespace Common

然后在你的命名空间:

#include "Common/EnumTools.h"

namespace MyNamespace {

enum class MyEnum {
    foo,
    bar = 4,
    baz = 42,
};

// Making this not have to be in the `Common` namespace took some thinking,
// but is a critical feature since otherwise there's no hope in keeping it sync'd with the enum.
inline constexpr auto allValuesArray(const MyEnum&, Common::EnumAllValuesTag) {
    return std::array{ MyEnum::foo, MyEnum::bar, MyEnum::baz };
}

} // namespace MyNamespace

然后在任何需要使用它的地方:

for (const auto& e : Common::AllValues<MyNamespace::MyEnum>) { ... }

所以即使你有typeded:

namespace YourNS {
using E = MyNamespace::MyEnum;
} // namespace YourNS

for (const auto& e : Common::AllValues<YourNS::E>) { ... }

我想不出比这更好的了,除了每个人都想要的实际语言功能。

未来工作:

您应该能够添加一个constexpr函数(以及一个元函数)来过滤Common::AllValues<E>,从而为枚举具有重复数值的情况提供一个Common::AllDistinctValues<E>,例如enum {foo = 0, bar = 0};。 我打赌有一种方法可以使用编译器的switch- coverage -all-enum-values来编写allValuesArray,这样如果枚举添加了一个值,它就会出错。

这里有一些非常易读且易于理解的方法,适用于弱类型的C和c++常规枚举,以及强类型的c++枚举类。

我建议使用-Wall -Wextra -Werror编译下面所有的例子。这为您提供了额外的安全性,如果您忘记在开关情况下覆盖任何枚举值,编译器将抛出编译时错误!这迫使您保持枚举定义和开关用例同步,这是代码的额外安全措施。只要你:

覆盖开关案例中的所有枚举值,和 没有默认开关大小写。 使用-Wall -Wextra -Werror旗帜构建。

我建议您遵循所有这3点,因为这是一个很好的实践,可以创建更好的代码。

1. 对于标准的弱类型C或c++ enum:

C定义(这也适用于c++):

typedef enum my_error_type_e 
{
    MY_ERROR_TYPE_SOMETHING_1 = 0,
    MY_ERROR_TYPE_SOMETHING_2,
    MY_ERROR_TYPE_SOMETHING_3,
    MY_ERROR_TYPE_SOMETHING_4,
    MY_ERROR_TYPE_SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    MY_ERROR_TYPE_count,
    // helpers for iterating over the enum
    MY_ERROR_TYPE_begin = 0,
    MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
} my_error_type_t;

c++定义:

enum my_error_type_t 
{
    MY_ERROR_TYPE_SOMETHING_1 = 0,
    MY_ERROR_TYPE_SOMETHING_2,
    MY_ERROR_TYPE_SOMETHING_3,
    MY_ERROR_TYPE_SOMETHING_4,
    MY_ERROR_TYPE_SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    MY_ERROR_TYPE_count,
    // helpers for iterating over the enum
    MY_ERROR_TYPE_begin = 0,
    MY_ERROR_TYPE_end = MY_ERROR_TYPE_count,
};

C或c++对弱类型枚举的迭代:

注意:通过my_error_type++递增枚举是不允许的——甚至在c风格的enum上也不允许,所以我们必须这样做:my_error_type = (my_error_type_t)(my_error_type + 1)。注意,my_error_type+ 1是允许的,然而,因为这个弱enum在这里会自动隐式强制转换为int类型,这样就可以不手动强制转换为int类型:my_error_type = (my_error_type_t)((int)my_error_type + 1)。

for (my_error_type_t my_error_type = MY_ERROR_TYPE_begin; 
        my_error_type < MY_ERROR_TYPE_end;
        my_error_type = (my_error_type_t)(my_error_type + 1)) 
{
    switch (my_error_type) 
    {
        case MY_ERROR_TYPE_SOMETHING_1:
            break;
        case MY_ERROR_TYPE_SOMETHING_2:
            break;
        case MY_ERROR_TYPE_SOMETHING_3:
            break;
        case MY_ERROR_TYPE_SOMETHING_4:
            break;
        case MY_ERROR_TYPE_SOMETHING_5:
            break;
        case MY_ERROR_TYPE_count:
            // This case will never be reached.
            break;
    }
}

2. 对于限定作用域的强类型c++枚举类:

c++定义:

enum class my_error_type_t
{
    SOMETHING_1 = 0,
    SOMETHING_2,
    SOMETHING_3,
    SOMETHING_4,
    SOMETHING_5,
    /// Not a valid value; this is the number of members in this enum
    count,
    // helpers for iterating over the enum
    begin = 0,
    end = count,
};

这个强类型枚举的c++迭代:

注意,强制递增枚举类变量需要额外的(size_t)强制转换(或者(int)也可以接受)!这里我还选择使用c++风格的static_cast<my_error_type_t>强制转换,但是C风格的(my_error_type_t)强制转换(如上所示)也可以。

for (my_error_type_t my_error_type = my_error_type_t::begin; 
        my_error_type < my_error_type_t::end;
        my_error_type = static_cast<my_error_type_t>((size_t)my_error_type + 1)) 
{
    switch (my_error_type) 
    {
        case my_error_type_t::SOMETHING_1:
            break;
        case my_error_type_t::SOMETHING_2:
            break;
        case my_error_type_t::SOMETHING_3:
            break;
        case my_error_type_t::SOMETHING_4:
            break;
        case my_error_type_t::SOMETHING_5:
            break;
        case my_error_type_t::count:
            // This case will never be reached.
            break;
    }
}

Also notice the scoping. In the C++ strongly-typed enum class I used my_error_type_t:: to access each scoped enum class member. But, in the C-style weakly-typed regular enum, very similar scoping can be achieved, as I demonstrated, simply be prefixing each enum member name with MY_ERROR_TYPE_. So, the fact that the C++ strongly-typed enum class adds scoping doesn't really add much value--it's really just a personal preference in that regard. And the fact that the C++ strongly-typed enum class has extra type-safety also has pros and cons. It may help you in some cases but it definitely makes incrementing the enum and iterating over it a pain-in-the-butt, which, honestly, means it is doing its job. By making it harder to increment the scoped enum class variable as though it was an integer, the C++ strongly-typed enum class is doing exactly what it was designed to do. Whether or not you want that behavior is up to you. Personally, I frequently do not want that behavior, and so it is not uncommon for me to prefer to use C-style enums even in C++.

参见:

[我的回答]在c++11中有一种方法通过索引初始化一个向量吗? [我的问答]在c++中迭代枚举类的常用方法是什么? 我对c++中枚举类(强类型enum)和常规enum(弱类型enum)之间的一些差异的回答:如何自动将强类型enum转换为int? 我的一些关于-Wall -Wextra -Werror和其他构建选项的个人笔记,来自我的eRCaGuy_hello_world回购。

#include <iostream>
#include <algorithm>

namespace MyEnum
{
  enum Type
  {
    a = 100,
    b = 220,
    c = -1
  };

  static const Type All[] = { a, b, c };
}

void fun( const MyEnum::Type e )
{
  std::cout << e << std::endl;
}

int main()
{
  // all
  for ( const auto e : MyEnum::All )
    fun( e );

  // some
  for ( const auto e : { MyEnum::a, MyEnum::b } )
    fun( e );

  // all
  std::for_each( std::begin( MyEnum::All ), std::end( MyEnum::All ), fun );

  return 0;
}