带有作用域枚举的数组索引(转换为整数)

Pot*_*ter 16 c++ arrays enums

C++ 11范围的枚举器(enum class语法)不转换为整数,因此它们不能直接用作数组索引.

在以这种方式使用它们时,获得范围界定的最佳方法是什么?

我已经提供了几个答案,但请添加更多想法!

Pot*_*ter 15

解决方案1:运算符重载.

这是我目前的最爱.重载一元operator+operator++分别在枚举类型中显式转换为整数类型和增量.

使用enumeration_traits模板,可以激活重载而不是复制样板代码.但是样板只是几个单行.

库代码(模板,请参见下面的非模板替代方案):

template< typename e >
struct enumeration_traits;

struct enumeration_trait_indexing {
    static constexpr bool does_index = true;
};

template< typename e >
constexpr
typename std::enable_if< enumeration_traits< e >::does_index,
    typename std::underlying_type< e >::type >::type
operator + ( e val )
    { return static_cast< typename std::underlying_type< e >::type >( val ); }

template< typename e >
typename std::enable_if< enumeration_traits< e >::does_index,
    e & >::type
operator ++ ( e &val )
    { return val = static_cast< e >( + val + 1 ); }
Run Code Online (Sandbox Code Playgroud)

用户代码:

enum class ducks { huey, dewey, louie, count };
template<> struct enumeration_traits< ducks >
    : enumeration_trait_indexing {};

double duck_height[ + ducks::count ];
Run Code Online (Sandbox Code Playgroud)

Boilerplate代码(如果不使用库,则遵循enum定义):

int operator + ( ducks val )
    { return static_cast< int >( val ); }

ducks &operator ++ ( ducks &val )
    { return val = static_cast< ducks >( + val + 1 ); }
Run Code Online (Sandbox Code Playgroud)

解决方案2:手动确定范围.

Scoped枚举器语法也适用于未enum class隐式(非)枚举,它隐式转换为int.将枚举隐藏在类或命名空间中并使用它进行导入typedefusing使其成为伪范围.

但是,如果多个枚举进入同一名称空间,则枚举器名称可能会发生冲突,因此您也可以使用类(或许多名称空间).

struct ducks_enum {
    enum ducks { huey, dewey, louie, count };
};
typedef ducks_enum::ducks ducks;

double duck_height[ ducks::count ]; // C++11
double duck_weight[ ducks_enum::count ]; // C++03
Run Code Online (Sandbox Code Playgroud)

这有一些好处.它适用于C++ 03,但只能使用语法ducks_enum::count.枚举器在结构内部是未作用域的,它可以用作任何经常使用枚举器的类的基础.


x-x*_*x-x 9

如果你的枚举是连续的,为什么要比它需要的更难?

enum class days
{
        monday,
        tuesday,
        wednesday,
        thursday,
        friday,
        saturday,
        sunday,
        count
};

....

const auto buffer_size = static_cast< std::size_t >( days::count );
char buffer[ buffer_size ];
buffer[ static_cast< std::size_t >( days::monday ) ] = 'M';
Run Code Online (Sandbox Code Playgroud)

或者如果你必须使用模板化的功能......

template< class enumeration >
constexpr std::size_t enum_count() noexcept
{
        static_assert( std::is_enum< enumeration >::value, "Not an enum" );
        return static_cast< std::size_t >( enumeration::count );
}

template< class enumeration >
constexpr std::size_t enum_index( const enumeration value ) noexcept
{
     static_assert( std::is_enum< enumeration >::value, "Not an enum" );
     return static_cast< std::size_t >( value )
}

...

char buffer[ enum_count< days >() ];
buffer[ enum_index( days::monday ) ] = 'M';
Run Code Online (Sandbox Code Playgroud)

  • 只是因为 `static_cast&lt; std::size_t &gt;( days::monday )` 语法很丑陋。如果我只想获得作用域的好处并且可以显式指定基础类型(例如“enum class Duck:int”),为什么要限制隐式转换?我理解“枚举类”的想法,但没有指定底层类型,但是如果我指定了,为什么我不能使用它?为什么禁止按位运算(同样使用显式基础类型)?从我的印象来看,新标准迫使我们使用旧的枚举......或者创建丑陋的解决方法。如果这么多人这样做,它应该成为一个标准...... (2认同)

Per*_*tte 6

最初的问题与使用枚举作为数组索引有关.不要试图将枚举转换为数组的索引,而是构建一个接受枚举作为索引的数组:

template <typename ValueType, typename Enumeration,
          Enumeration largest_enum = Enumeration::Count,
          int largest = static_cast <int> (largest_enum)>
class EnumeratedArray {
    ValueType underlying [static_cast <int> (largest_enum)];
public:
    using value_type = ValueType;
    using enumeration_type = Enumeration;
    EnumeratedArray () {
        for (int i = 0; i < largest; i++) {
            underlying [i] = ValueType {};
        }
    }
    inline ValueType &operator[] (const Enumeration index) {
        assert (static_cast <int> (index) >= 0 && static_cast <int> (index) < largest);
        return underlying [static_cast <const int> (index)];
    }
    inline const ValueType &operator[] (const Enumeration index) const {
        assert (static_cast <int> (index) >= 0 && static_cast <int> (index) < largest);
        return underlying [static_cast <const int> (index)];
    }
};
Run Code Online (Sandbox Code Playgroud)

所以现在,以早期的鸭子为例:

enum class ducks { huey, dewey, louie, count };
EnumeratedArray<double, ducks, ducks::count> duck_height;
duck_height [ducks::huey] = 42.0;
Run Code Online (Sandbox Code Playgroud)

如果鸭子的值大写不同,那么大小可以默认:

enum class Ducks { Huey, Dewey, Louie, Count };
EnumeratedArray<double, Ducks> duck_height;
duck_height [Ducks::Huey] = 42.0;
Run Code Online (Sandbox Code Playgroud)

除了避免枚举变形之外,由于to-index转换隐藏在实现中,因此枚举不会冒险错误地成为代码中其他点的整数,也不会无意中通过整数索引数组.

EnumeratedArray用于pianod2,在src/common中.更广泛的版本包括模板魔术,只显式默认初始化普通旧数据类型,构造函数将所有元素初始化为指定值,以及文档注释.