C ++ 11范围的枚举器(enum class
语法)不会转换为整数,因此它们不能直接用作数组索引。
以这种方式使用范围时,获得范围界定的最佳方法是什么?
我提供了几个答案,但请添加更多想法!
答案 0 :(得分:15)
这是我目前的最爱。重载一元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 ); }
用户代码:
enum class ducks { huey, dewey, louie, count };
template<> struct enumeration_traits< ducks >
: enumeration_trait_indexing {};
double duck_height[ + ducks::count ];
Boilerplate代码(如果不使用库,则遵循enum
定义):
int operator + ( ducks val )
{ return static_cast< int >( val ); }
ducks &operator ++ ( ducks &val )
{ return val = static_cast< ducks >( + val + 1 ); }
Scoped枚举器语法也适用于无范围(非enum class
)枚举,它隐式转换为int
。将枚举隐藏在类或命名空间中并使用typedef
或using
导入它会使其成为伪范围。
但是如果多个枚举进入同一个名称空间,枚举器名称可能会发生冲突,所以你也可以使用一个类(或许多名称空间)。
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
这有一些好处。 适用于C ++ 03 ,但只能使用语法ducks_enum::count
。枚举器在结构体内是未作用域的,它可以用作经常使用枚举器的任何类的基础。
答案 1 :(得分: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';
或者如果你必须使用模板化函数......
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';
答案 2 :(得分: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)];
}
};
现在,以早期的鸭子为例:
enum class ducks { huey, dewey, louie, count };
EnumeratedArray<double, ducks, ducks::count> duck_height;
duck_height [ducks::huey] = 42.0;
如果鸭子的值大写不同,那么大小可以默认:
enum class Ducks { Huey, Dewey, Louie, Count };
EnumeratedArray<double, Ducks> duck_height;
duck_height [Ducks::Huey] = 42.0;
除了避免枚举扭曲之外,由于to-index转换隐藏在实现中,因此枚举不会冒险错误地在代码中的其他点成为整数,也不会无意中通过整数索引数组。 / p>
EnumeratedArray用于pianod2,在src / common中。更广泛的版本包括模板魔术,只显式默认初始化普通旧数据类型,构造函数将所有元素初始化为指定值,以及文档注释。
答案 3 :(得分:1)
我正在实施DrTwox解决方案的组合以及Potatoswatter解决方案的类型安全性。必须显式定义枚举类以允许索引,并且还定义了size():
#include <iostream>
template< typename T >
class EnumClassTraits;
struct EnumClassTraitIndexing {
static constexpr bool does_index = true;
};
template<typename T>
constexpr
typename std::enable_if<EnumClassTraits<T>::does_index,
typename std::underlying_type<T>::type>::type enum_size() noexcept {
return EnumClassTraits<T>::size();
}
template<typename T>
typename std::enable_if<EnumClassTraits<T>::does_index,
typename std::underlying_type<T>::type>::type enum_index(T enum_key) noexcept {
return static_cast<typename std::underlying_type<T>::type>(enum_key);
}
enum class Days {Mon, Tue, Wed, Thu, Fri, Sat, Sun};
template<>
struct EnumClassTraits<Days> : EnumClassTraitIndexing {
static constexpr std::underlying_type<Days>::type size() {
return static_cast<std::underlying_type<Days>::type>(Days::Sun)+1;
}
};
int main(int argc, char* argv[]) {
Days days[enum_size<Days>()] = {Days::Mon, Days::Tue, Days::Wed, Days::Thu, Days::Fri, Days::Sat, Days::Sun};
const char* days_to_string[enum_size<Days>()] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
for (auto day : days) {
std::cout << days_to_string[enum_index(day)] << std::endl;
}
}
答案 4 :(得分:1)
或者,您可以将array
替换为map
,这也意味着您可以删除count
之类的最大枚举:
enum class days
{
monday,
tuesday,
wednesday,
thursday,
friday,
saturday,
sunday
};
int main(int argc, char* argv[])
{
std::map<days, const char*> days_to_string =
{{days::monday, "Monday"},
{days::tuesday, "Tuesday"},
{days::wednesday, "Wednesday"},
{days::thursday, "Thursday"},
{days::friday, "Friday"}};
for (auto day : days)
{
std::cout << days_to_string[day] << std::endl;
}
}