我试图通过迭代枚举Suit
和Rank
来创建一副牌(我知道那里没有很好的方法可以迭代枚举但是我没有&# 39;看到另一种选择)。我通过在每个枚举的末尾添加一个枚举器enum_count
来做到这一点,其值用于表示枚举的长度和结尾。
#include <vector>
using namespace std;
enum class Suit: int {clubs, diamonds, hearts, spades, enum_count};
enum class Rank: int {one, two, three, four, five, six, seven, eight,
nine, ten, jack, queen, king, ace, enum_count};
struct Card {
Suit suit;
Rank rank;
};
class Deck{
vector<Card> cards{};
public:
Deck();
};
Deck::Deck() {
// ERROR ON THE BELOW LINE
for (Suit suit = Suit::clubs; suit < Suit::enum_count; suit++) {
for (Rank rank = Rank::one; rank < Rank::enum_count; rank++) {
Card created_card;
created_card.suit = suit;
created_card.rank = rank;
cards.push_back(created_card);
};
};
};
但是,当我尝试遍历枚举时,编译器并不喜欢我试图在for循环中递增suit++
和rank++
,说明:
card.cpp|24|error: no ‘operator++(int)’ declared for postfix ‘++’ [-fpermissive]|
card.cpp|25|error: no ‘operator++(int)’ declared for postfix ‘++’ [-fpermissive]|
创建一副卡片而不丢弃有用的枚举数据结构的最佳方法是什么?
答案 0 :(得分:5)
我建议做一些与众不同的事情。
创建一个Suit
和一个Rank
的向量,并使用STL的力量循环它们
const std::vector<Suit> v_suit {Suit::clubs, Suit::diamonds, Suit::hearts, Suit::spades};
const std::vector<Rank> v_rank {Rank::one, Rank::two, Rank::three, Rank::four, Rank::five,
Rank::six, Rank::seven, Rank::eight, Rank::nine, Rank::ten, Rank::jack,
Rank::queen, Rank::king, Rank::ace};
是的,你必须输入两次,但这允许你使用你想要的任何值(即不连续),而不是使用像enum_count
这样的尴尬的东西(你想要什么卡?给我一个钻石enum_count !!),不需要进行强制转换,并使用提供给std::vector
的迭代器。
使用它们:
for(const auto & s : v_suit)
for (const auto & r : v_rank)
cards.push_back({s,r});
答案 1 :(得分:4)
您可以将suit
和rank
变量转换为int&
并将其增加。
for (Suit suit = Suit::clubs; suit < Suit::enum_count; ((int&)suit)++) {
for (Rank rank = Rank::one; rank < Rank::enum_count; ((int&)rank)++) {
然而,这可能会导致一些问题,例如为枚举条目指定值时。
您还可以使用正确的类型创建一个为您执行此操作的小功能:
template <typename T>
T& increment(T& value)
{
static_assert(std::is_integral<std::underlying_type_t<T>>::value, "Can't increment value");
((std::underlying_type_t<T>&)value)++;
return value;
}
Deck::Deck() {
bool a = std::is_integral<std::underlying_type_t<Suit>>::value;
// ERROR ON THE BELOW LINE
for (Suit suit = Suit::clubs; suit < Suit::enum_count; increment(suit)) {
for (Rank rank = Rank::one; rank < Rank::enum_count; increment(rank)) {
Card created_card;
created_card.suit = suit;
created_card.rank = rank;
cards.push_back(created_card);
};
};
};
答案 2 :(得分:3)
使用C ++ 11,您可以使用基于范围的for循环。您所需要做的就是用operator++
,operator!=
和operator*
定义一个迭代器类,并将begin
或end
定义为成员函数或自由函数。 / p>
这里是使用EnumRange
类的示例,该类包括Iterator
类和begin
或end
成员函数。该类假定T
是一个enum class
,其连续值从0开始并在MAX
结束。 MAX
声明用于避免向枚举添加无效值,例如enum_count
。如果enum class
没有定义MAX
,则代码将无法编译。
template <class T>
struct EnumRange {
struct Iterator {
explicit Iterator(int v) : value(v) {}
void operator++() { ++value; }
bool operator!=(Iterator rhs) { return value != rhs.value; }
T operator*() const { return static_cast<T>(value); }
int value = 0;
};
Iterator begin() const { return Iterator(0); }
Iterator end() const { return Iterator(static_cast<int>(T::MAX) + 1); }
};
这使您可以简单地编写:
enum class Suit {clubs, diamonds, hearts, spades, MAX=spades};
enum class Rank {one, two, three, four, five, six, seven, eight,
nine, ten, jack, queen, king, ace, MAX=ace};
for(const Suit s : EnumRange<Suit>())
for (const Rank r : EnumRange<Rank>())
cards.push_back({s,r});
此方法的优点是它避免了每次要在枚举上进行迭代时都需要定义/分配映射或向量的情况。相反,EnumRange::Iterator
类存储单个整数,并且自动支持对枚举的任何更改。另外,由于我们已经定义operator*
来将整数转换为枚举类型T
,所以我们知道基于范围的for循环的变量类型是枚举。
总而言之,结果就是易于理解的表达式for(MyEnum s : EnumRange<MyEnum>())
。
答案 3 :(得分:2)
您无法在enum class
中使用此功能。您必须使用旧样式enum
。
如果你坚持要使用它们。我可以建议你一个不使用的坏解决方案(除非你不会告诉任何人我建议它):
for (Rank rank = Rank::one;
static_cast<int>(rank) < static_cast<int>(Rank::enum_count);
rank = static_cast<Rank>(static_cast<int>(rank)+1)){
};
答案 4 :(得分:2)
回复old_mountain's answer的其他答案:
在某些情况下,您可以通过使用固定数组来防止忘记向列表中添加新值。这个问题的主要问题是初始化程序接受的参数少于指定的参数,但您可以解决这个问题:
template<typename T, typename...Args>
struct first_type
{
using type = T;
};
template<typename... Args>
std::array<typename first_type<Args...>::type, sizeof...(Args)> make_array(Args&&... refs)
{
return std::array<typename first_type<Args...>::type, sizeof...(Args)>{ { std::forward<Args>(refs)... } };
}
我在这个问题中找到了make_array
的灵感,但修改了它:How to emulate C array initialization "int arr[] = { e1, e2, e3, ... }" behaviour with std::array?
它的作用是使用第一个参数来找出std::array
应该是什么类型以及获得实际大小的参数数量。
因此返回类型为std::array<firstType, numArgs>
。
现在宣布你的名单如下:
const std::array<Suit, (size_t)Suit::enum_count> SuitValues =
make_array(Suit::clubs, Suit::diamonds, Suit::hearts, Suit::spades);
const std::array<Rank, (size_t)Rank::enum_count> RankValues =
make_array(Rank::one, Rank::two, Rank::three, Rank::four,
Rank::five, Rank::six, Rank::seven, Rank::eight,
Rank::nine, Rank::ten, Rank::jack, Rank::queen,
Rank::king, Rank::ace);
当您向数组添加值时,您enum_count
或您用作分隔符的任何值都会发生变化,因此make_array
的分配将因std::array
的大小而失败不同(导致不同的类型)。
示例:强>
如果您只是添加新的Suit
,请说hexa
enum class Suit : int { clubs, diamonds, hearts, spades, hexa, enum_count };
编译器将失败:
cannot convert from 'std::array<T,0x04>' to 'const std::array<Suit,0x05>'
我必须承认,我对此解决方案并不十分满意,因为它需要在数组声明中对size_t
进行非常丑陋的转换。
答案 5 :(得分:1)
我还希望根据使用C ++ 11和C ++ 14功能创建map<enum,string>
的{{3}}分享我的方法,代码如下:
// Shortcut to the map
template <typename ENUM>
using enum_map = std::map<ENUM, const std::string>;
// Template variable for each enumerated type
template <typename ENUM>
enum_map<ENUM> enum_values{};
// Empty function to end the initialize recursion
void initialize(){}
// Recursive template which initializes the enum map
template <typename ENUM, typename ... args>
void initialize(const ENUM value, const char *name, args ... tail)
{
enum_values<ENUM>.emplace(value, name);
initialize(tail ...);
}
使用此模板,我们可以通过以下方式更改Deck
构造函数:
Deck::Deck() {
for (const auto &S : enum_values<Suit>) {
for (const auto &R : enum_values<Rank>) {
Card created_card;
created_card.suit = S.first;
created_card.rank = R.first;
cards.push_back(created_card);
};
};
};
为了使整个工作有效,唯一的要求就是以这种方式调用initialize
函数:
initialize
(
Suit::clubs, "Clubs",
Suit::diamonds, "Diamonds",
Suit::hearts, "Hearts",
Suit::spades, "Spades",
Rank::one, "1",
Rank::two, "2",
Rank::three, "3",
Rank::four, "4",
Rank::five, "5",
Rank::six, "6",
Rank::seven, "7",
Rank::eight, "8",
Rank::nine, "9",
Rank::ten, "10",
Rank::jack, "J",
Rank::queen, "Q",
Rank::king, "K",
Rank::ace, "A"
);
您可以查看 previous answer of mine 。