#include <iostream>
struct a {
enum LOCAL_A { A1, A2 };
};
enum class b { B1, B2 };
int foo(int input) { return input; }
int main(void) {
std::cout << foo(a::A1) << std::endl;
std::cout << foo(static_cast<int>(b::B2)) << std::endl;
}
Le a::LOCAL_A
correspond à ce que l'énumération fortement typée essaie de réaliser, mais il existe une petite différence: les énumérations normales peuvent être converties en type entier, alors que les énumérations fortement typées ne peuvent le faire sans conversion.
Alors, est-il possible de convertir une valeur enum fortement typée en un type entier sans transtypage? Si oui comment?
Enums fortement typés visant à résoudre de multiples problèmes et pas seulement le problème de cadrage comme vous l'avez mentionné dans votre question:
Ainsi, il est impossible de convertir implicitement une énumération fortement typée en entiers, ou même son type sous-jacent - c'est l'idée. Vous devez donc utiliser static_cast
pour rendre la conversion explicite.
Si votre seul problème est la portée et que vous voulez vraiment avoir une promotion implicite sur les entiers, il vaut mieux utiliser une énumération non typée avec l'étendue de la structure dans laquelle elle est déclarée.
Comme d'autres l'ont dit, vous ne pouvez pas avoir de conversion implicite, et c'est une conception.
Si vous le souhaitez, vous pouvez éviter de spécifier le type sous-jacent dans la distribution.
template <typename E>
constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
return static_cast<typename std::underlying_type<E>::type>(e);
}
std::cout << foo(to_underlying(b::B2)) << std::endl;
Une version C++ 14 de la réponse fournie par R. Martinho Fernandes serait:
#include <type_traits>
template <typename E>
constexpr auto to_underlying(E e) noexcept
{
return static_cast<std::underlying_type_t<E>>(e);
}
Comme avec la réponse précédente, cela fonctionnera avec n'importe quel type d'énum et de type sous-jacent. J'ai ajouté le mot clé noexcept
car il ne lève jamais d'exception.
Mettre à jour
Ceci apparaît également dans Effective Modern C++ de Scott Meyers . Voir le point 10 (il est détaillé dans les dernières pages du point dans ma copie du livre).
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <type_traits>
namespace utils
{
namespace details
{
template< typename E >
using enable_enum_t = typename std::enable_if< std::is_enum<E>::value,
typename std::underlying_type<E>::type
>::type;
} // namespace details
template< typename E >
constexpr inline details::enable_enum_t<E> underlying_value( E e )noexcept
{
return static_cast< typename std::underlying_type<E>::type >( e );
}
template< typename E , typename T>
constexpr inline typename std::enable_if< std::is_enum<E>::value &&
std::is_integral<T>::value, E
>::type
to_enum( T value ) noexcept
{
return static_cast<E>( value );
}
} // namespace utils
int main()
{
enum class E{ a = 1, b = 3, c = 5 };
constexpr auto a = utils::underlying_value(E::a);
constexpr E b = utils::to_enum<E>(5);
constexpr auto bv = utils::underlying_value(b);
printf("a = %d, b = %d", a,bv);
return 0;
}
Non, il y a pas de manière naturelle.
En fait, une des motivations derrière avoir fortement typé enum class
en C++ 11 est d'empêcher leur conversion silencieuse en int
.
J'espère que cela vous aide ou aide quelqu'un d'autre
enum class EnumClass : int //set size for enum
{
Zero, One, Two, Three, Four
};
union Union //This will allow us to convert
{
EnumClass ec;
int i;
};
int main()
{
using namespace std;
//convert from strongly typed enum to int
Union un2;
un2.ec = EnumClass::Three;
cout << "un2.i = " << un2.i << endl;
//convert from int to strongly typed enum
Union un;
un.i = 0;
if(un.ec == EnumClass::Zero) cout << "True" << endl;
return 0;
}
La raison de l'absence de conversion implicite (par conception) a été donnée dans d'autres réponses.
J'utilise personnellement unary operator+
pour la conversion de classes enum en leur type sous-jacent:
template <typename T>
constexpr auto operator+(T e) noexcept
-> std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>>
{
return static_cast<std::underlying_type_t<T>>(e);
}
Ce qui donne assez peu de "frais généraux de frappe":
std::cout << foo(+b::B2) << std::endl;
Où j'utilise réellement une macro pour créer des énumérations et les fonctions d'opérateur en une seule fois.
#define UNSIGNED_ENUM_CLASS(name, ...) enum class name : unsigned { __VA_ARGS__ };\
inline constexpr unsigned operator+ (name const val) { return static_cast<unsigned>(val); }
La réponse courte est que vous ne pouvez pas, comme indiqué ci-dessus. Mais dans mon cas, je ne voulais tout simplement pas encombrer l’espace de noms, mais toujours avoir des conversions implicites.
#include <iostream>
using namespace std;
namespace Foo {
enum Foo { bar, baz };
}
int main() {
cout << Foo::bar << endl; // 0
cout << Foo::baz << endl; // 1
return 0;
}
La sorte d'espacement de noms ajoute une couche de sécurité de type alors que je n'ai pas besoin de convertir statiquement des valeurs enum sur le type sous-jacent.
Cela semble impossible avec le enum class
natif, mais vous pouvez probablement vous moquer d'un enum class
avec un class
:
Dans ce cas,
enum class b
{
B1,
B2
};
serait équivalent à:
class b {
private:
int underlying;
public:
static constexpr int B1 = 0;
static constexpr int B2 = 1;
b(int v) : underlying(v) {}
operator int() {
return underlying;
}
};
Ceci est essentiellement équivalent à l'original enum class
. Vous pouvez directement renvoyer b::B1
dans une fonction de type de retour b
. Vous pouvez faire switch case
avec, etc.
Et dans l'esprit de cet exemple, vous pouvez utiliser des modèles (éventuellement avec d'autres éléments) pour généraliser et simuler tout objet possible défini par la syntaxe enum class
.
Comme beaucoup l'ont dit, il n'y a aucun moyen de convertir automatiquement sans ajouter des frais généraux et trop de complexité, mais vous pouvez réduire votre frappe et la rendre plus belle en utilisant lambdas si certains acteurs sont utilisés un peu trop dans un scénario. Cela ajouterait un peu d'appel de surcharge de fonction, mais rendrait le code plus lisible par rapport aux longues chaînes static_cast, comme on peut le voir ci-dessous. Cela peut ne pas être utile à l’échelle du projet, mais seulement à l’échelle de la classe.
#include <bitset>
#include <vector>
enum class Flags { ......, Total };
std::bitset<static_cast<unsigned int>(Total)> MaskVar;
std::vector<Flags> NewFlags;
-----------
auto scui = [](Flags a){return static_cast<unsigned int>(a); };
for (auto const& it : NewFlags)
{
switch (it)
{
case Flags::Horizontal:
MaskVar.set(scui(Flags::Horizontal));
MaskVar.reset(scui(Flags::Vertical)); break;
case Flags::Vertical:
MaskVar.set(scui(Flags::Vertical));
MaskVar.reset(scui(Flags::Horizontal)); break;
case Flags::LongText:
MaskVar.set(scui(Flags::LongText));
MaskVar.reset(scui(Flags::ShorTText)); break;
case Flags::ShorTText:
MaskVar.set(scui(Flags::ShorTText));
MaskVar.reset(scui(Flags::LongText)); break;
case Flags::ShowHeading:
MaskVar.set(scui(Flags::ShowHeading));
MaskVar.reset(scui(Flags::NoShowHeading)); break;
case Flags::NoShowHeading:
MaskVar.set(scui(Flags::NoShowHeading));
MaskVar.reset(scui(Flags::ShowHeading)); break;
default:
break;
}
}