c++ - enumerations - Indización de matriz(conversión a entero) con enumeración de ámbito
enumerations c++ (5)
Solución 1: Sobrecarga del operador.
Este es mi favorito actual. Sobrecargue el operator+
unario operator+
y el operator++
para convertir explícitamente a tipo integral e incremento dentro del tipo enumerado, respectivamente.
Usando una plantilla de enumeration_traits
, las sobrecargas se pueden activar en lugar de copiar el código de repetición. Pero el repetitivo es solo un par de frases.
Código de la biblioteca (plantillas, ver a continuación para la alternativa sin plantilla):
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 ); }
Codigo de usuario:
enum class ducks { huey, dewey, louie, count };
template<> struct enumeration_traits< ducks >
: enumeration_trait_indexing {};
double duck_height[ + ducks::count ];
Código de repetición (si no utiliza la biblioteca, sigue la definición de enum
):
int operator + ( ducks val )
{ return static_cast< int >( val ); }
ducks &operator ++ ( ducks &val )
{ return val = static_cast< ducks >( + val + 1 ); }
Solución 2: alcance manual.
La sintaxis del enumerador de ámbito también funciona en las enum class
sin ámbito (no enum class
), que se convierten implícitamente a int
. Al ocultar la enumeración dentro de una clase o espacio de nombres e importarla con typedef
o using
hace pseudo-ámbito.
Pero si varias enumeraciones entran en el mismo espacio de nombres, los nombres del enumerador pueden chocar, por lo que también puede usar una clase (o muchos espacios de nombres).
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
Esto tiene algunos beneficios. Funciona con C ++ 03 , pero solo con la sintaxis ducks_enum::count
. Los enumeradores no tienen ámbito dentro de la estructura, y se pueden usar como base para cualquier clase que haga uso frecuente de los enumeradores.
Los enumeradores de ámbito de C ++ 11 (sintaxis de enum class
) no se convierten a enteros, por lo que no pueden usarse directamente como índices de matriz.
¿Cuál es la mejor manera de obtener el beneficio del alcance cuando se usan de esta manera?
He proporcionado un par de respuestas, ¡pero por favor agregue más ideas!
¿Por qué hacerlo más difícil de lo que debe ser si su enumeración es consecutiva?
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'';
O si debes usar funciones de plantilla ...
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'';
Alternativamente, puede reemplazar su array
con un map
, lo que también significa que puede deshacerse de la cantidad máxima de enumeración como:
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;
}
}
Estoy implementando una combinación de la solución de DrTwox más el tipo de seguridad de la solución de Potatoswatter. La clase de enumeración debe definirse explícitamente para permitir la indexación, con size () también definido:
#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;
}
}
La pregunta original relacionada con el uso de la enumeración como un índice de matriz. En lugar de intentar convertir la enumeración en un índice para una matriz, cree una matriz que acepte la enumeración como su índice:
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)];
}
};
Así que ahora, con el ejemplo anterior de los patos:
enum class ducks { huey, dewey, louie, count };
EnumeratedArray<double, ducks, ducks::count> duck_height;
duck_height [ducks::huey] = 42.0;
Si los valores de los patos se hubieran capitalizado de manera diferente, el tamaño podría ser predeterminado
enum class Ducks { Huey, Dewey, Louie, Count };
EnumeratedArray<double, Ducks> duck_height;
duck_height [Ducks::Huey] = 42.0;
Además de evitar las contorsiones de la enumeración, ya que la conversión a índice está oculta en la implementación, la enumeración no corre el riesgo de convertirse erróneamente en un número entero en otros puntos de su código, ni puede indizar inadvertidamente la matriz mediante un número entero.
EnumeratedArray se utiliza en pianod2, en src / common. Una versión más extensa incluye plantilla mágica para inicializar explícitamente de forma predeterminada los tipos de datos antiguos, un constructor para inicializar todos los elementos a un valor específico y comentarios de documentos.