C++11 スコープ列挙子 (enum class
構文) は整数に変換されないため、配列インデックスとして直接使用することはできません。
この方法でスコープを使用する場合、スコープの利点を得る最良の方法は何ですか?
いくつかの回答を提供しましたが、さらにアイデアを追加してください。
これは私の現在のお気に入りです。unaryoperator+
をオーバーロード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 ];
ボイラープレート コード (ライブラリを使用しない場合は、enum
定義に従います):
int operator + ( ducks val )
{ return static_cast< int >( val ); }
ducks &operator ++ ( ducks &val )
{ return val = static_cast< ducks >( + val + 1 ); }
enum class
スコープ付き列挙子の構文は、暗黙的に に変換されるスコープなし (非 ) 列挙でも機能しますint
。typedef
クラスまたは名前空間内に列挙を隠し、 orを使用してインポートすると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
。列挙子は構造体内でスコープが設定されておらず、列挙子を頻繁に使用する任意のクラスのベースとして使用できます。
列挙が連続している場合、必要以上に難しくするのはなぜですか?
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';
列挙型を配列インデックスとして使用することに関連する元の質問。列挙を配列のインデックスに変換しようとする代わりに、列挙をインデックスとして受け入れる配列を作成します。
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;
列挙型のゆがみを回避することに加えて、インデックスへの変換は実装で隠されているため、列挙型がコード内の他のポイントで誤って整数になるリスクはなく、整数を介して誤って配列にインデックスを付けることもできません。
EnumeratedArray は、src/common で、pianod2 で使用されます。より広範なバージョンには、単純な古いデータ型のみを明示的にデフォルトで初期化するテンプレート マジック、すべての要素を指定された値に初期化するコンストラクタ、およびドキュメント コメントが含まれています。
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;
}
}