如何使用C ++枚举类枚举器作为std :: array索引而不使用显式转换

时间:2017-12-25 22:32:42

标签: c++ arrays c++11 enums

当我想引用特定索引时,我想使用C ++枚举类作为std:array索引而不调用显式转换。

我还使用了一个固定大小的std :: array的typedef。

typedef std::array<int, 3> MyType;
enum class MyEnum {
  ENUMERATOR0 = 0,
  ENUMERATOR1 = 1,
  ENUMERATOR2 = 2,
};

所以不要使用:

MyType my_type = {0};
my_type[static_cast<int>(MyEnum::ENUMERATOR0)] = 42;

我想用:

my_type[MyEnum::ENUMERATOR0] = 42;

因此,我假设需要重载myType(std :: array)类型的下标运算符。但是,我无法弄清楚,在我的情况下如何重载下标运算符。为简单起见,我想避免使用类而不是typedef。 我怎么能这样做?

5 个答案:

答案 0 :(得分:1)

您不能在您不拥有的类型上覆盖[]

请参阅http://en.cppreference.com/w/cpp/language/operators - operaror[]不能非成员重载。

你可以这样做:

struct MyType: std::array<int, 3>{
  using base= std::array<int, 3>;
  constexpr MyType():base{}{}
  template<class A0, class...Args,
    std::enable_if_t<!std::is_same<MyType, std::decay_t<A0>>{}, bool>=true
  >
  constexpr MyType(A0&& a0, Args&&...args):base{{std::forward<A0>(a0), std::forward<Args>(args)...}}{}
  using base::operator[];
  constexpr int& operator[](MyEnum e){ return operator[](static_cast<std::size_t>(e)); }
  constexpr int const& operator[](MyEnum e)const{ return operator[](static_cast<std::size_t>(e)); }
};

这很接近。取代

MyType x={{1,2,3}};

MyType x={1,2,3};

答案 1 :(得分:1)

我为此找到了一个很好的解决方案。您既可以将枚举类用作数组中的索引,又可以通过子类化std :: array并覆盖其operator []方法来获得确保类型安全(即,防止使用错误的枚举类型作为索引)的额外好处。

这里是一个例子。

您可以这样定义enum_array:

#include <array>

// this is a new kind of array which accepts and requires its indices to be enums
template<typename E, class T, std::size_t N>
class enum_array : public std::array<T, N> {
public:
    T & operator[] (E e) {
        return std::array<T, N>::operator[]((std::size_t)e);
    }

    const T & operator[] (E e) const {
        return std::array<T, N>::operator[]((std::size_t)e);
    }
};

您可以像这样使用它:

int main() {
    enum class Fruit : unsigned int {
        Apple,
        Kiwi
    };

    enum class Vegetable : unsigned int {
        Carrot,
        Potato
    };

    // Old way:
    std::array<int, 3> old_fruits;
    std::array<int, 3> old_veggies;

    old_fruits[(int)Fruit::Apple] = 3;          // compiles but "ugly"
    old_veggies[(int)Vegetable::Potato] = 7;    // compiles but "ugly"

    old_fruits[(int)Vegetable::Potato] = 3;     // compiles but shouldn't compile!
    old_fruits[2] = 6;                          // compiles but may or may not be desirable

    // New way:
    enum_array<Fruit, int, 3> fruits;
    enum_array<Vegetable, int, 3> veggies;

    fruits[Fruit::Apple] = 3;
    veggies[Vegetable::Potato] = 7;

    // fruits[Vegetable::Potato] = 3;   // doesn't compile :)
    // fruits[2] = 6;                   // doesn't compile
    // fruits[(int)Fruit::Apple] = 3;   // doesn't compile
} 

答案 2 :(得分:0)

我不喜欢enum class的当前行为。在我确实需要在指定枚举值时强制使用枚举名称的情况下,我使用以下代码。

//Utilities.hpp
#define SETUP_ENUM_STRUCT_ASSIGNMENTS(EnumStruct, EType)        \
    EType val;                                                  \
    EnumStruct(){}                                              \
    EnumStruct(EType p_eVal):val(p_eVal) {}                     \
    operator EType () const { return val; }                     \
    void operator=(EType p_eVal) { val = p_eVal; }  

#define ENUM_STRUCT(EName, ...)                        \
struct EName {                                         \
    enum Type {                                        \
        __VA_ARGS__                                    \
    };                                                 \
    SETUP_ENUM_STRUCT_ASSIGNMENTS(EName, Type)         \
};


//main.cpp
int main(){

    ENUM_STRUCT( EFruit,   //<--this line is weird and looks unusual
        APPLE,             //    for enum definition, but this tool
        GRAPES,            //    does the work I need so it's OK for 
        ORANGE,            //    me despite the code weirdness.

        COUNT
    )

    std::array<int, EFruit::COUNT> listF;  //<--no need for any type-casting.

    listF[EFruit::APPLE] = 100;            //<--looks cleaner like enum class with
                                          //     no type restriction.

    return 0;
}

在特殊情况下,我使用ENUM_STRUCT宏主要是为了提高可读性(这对调试很有帮助)。在大多数情况下,我使用普通的enum。由于限制,我很少使用enum class

答案 3 :(得分:0)

信誉不足,因此我无法发表评论... 在jordi的解决方案上,替换旧式的强制类型转换:

(std::size_t)e

具有新样式:

static_cast<std::size_t>(e)

这将避免编译警告...

答案 4 :(得分:0)

您可以使用 findDOMNode 模拟 enum

class

然后你可以像这样使用它:

struct MyEnum {
    enum {
        ENUMERATOR0 = 0,
        ENUMERATOR1 = 1,
        ENUMERATOR2 = 2,
    } _val;
    constexpr MyEnum(decltype(_val) value) noexcept : 
        _val{value}
    {
    }
    constexpr explicit MyEnum(int value) noexcept : 
        _val{static_cast<decltype(_val)>(value)}
    {
    }
    [[nodiscard]] constexpr operator int() const noexcept
    {
        return _val;
    }
};

(godbolt)