2016-02-10 30 views
6

Tôi đang cố gắng tạo một cỗ bài bằng cách lặp lại các enums SuitRank (Tôi biết không có cách nào tuyệt vời để lặp lại các enums nhưng tôi không thấy thay thế). Tôi đã làm điều này bằng cách thêm một điều tra enum_count vào cuối mỗi enum, có giá trị có nghĩa là đại diện cho chiều dài và kết thúc của enum.Làm thế nào để sử dụng các giá trị lớp enum như là một phần của vòng lặp for?

#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); 
     }; 
    }; 
}; 

Tuy nhiên, khi tôi cố gắng để lặp qua enum, trình biên dịch không thích mà tôi đang cố gắng để tăng suit++rank++ trong cho vòng lặp, trong đó nêu:

card.cpp|24|error: no ‘operator++(int)’ declared for postfix ‘++’ [-fpermissive]| 
card.cpp|25|error: no ‘operator++(int)’ declared for postfix ‘++’ [-fpermissive]| 

Cách tốt nhất để đi về việc tạo ra một cỗ bài mà không vứt bỏ các cấu trúc dữ liệu enum hữu ích là gì?

+0

[Liên quan?] (Http://stackoverflow.com/questions/261963/how-can-i-iterate-over-an-enum) –

+1

@JamesRoot Tôi đã cố gắng sao chép nhiều giải pháp được đề xuất trong câu hỏi đó (mặc dù hầu hết các câu trả lời đều có về các enums chưa được kiểm duyệt) và những người khác tôi tìm thấy trên internet, nhưng vô ích. Vì vậy, tôi đăng ở đây nỗ lực tốt nhất của tôi. – DBedrenko

+1

@NewWorld Tôi đã hỏi [một cái gì đó tương tự] (http://stackoverflow.com/questions/13971544/using-enum-in-loops-and-value-consistency) một số thời gian trước đây. –

Trả lời

4

tôi sẽ khuyên bạn nên làm một cái gì đó khác nhau. Tạo một vector của Suit và một để Rank, và vòng qua chúng bằng cách sử dụng sức mạnh của 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}; 

Có, bạn phải gõ họ hai lần, nhưng điều này cho phép bạn sử dụng bất cứ giá trị nào bạn muốn cho họ (ví dụ: Không liên tiếp), không sử dụng những thứ khó xử như enum_count (Bạn muốn dùng loại thẻ nào? Hãy cho tôi một viên kim cương enum_count !!), không cần đúc và sử dụng các trình vòng lặp được cung cấp cho std::vector.

Để sử dụng chúng:

for(const auto & s : v_suit) 
    for (const auto & r : v_rank) 
     cards.push_back({s,r}); 
+0

Cảm ơn bạn đã giúp tôi :) Tôi thích giải pháp này tốt nhất cho đến nay; nó là sạch nhất, và phần xấu xí duy nhất là enum và vector phải được giữ đồng bộ theo cách thủ công bởi lập trình viên nếu bao giờ một trong số chúng thay đổi, nhưng tôi nghĩ điều này thực sự là chấp nhận được. – DBedrenko

+0

Nó sẽ không tốt hơn để làm cho các vectơ const? Ý tôi là, không ai có thể thay đổi chúng trong thời gian chạy. –

+0

Tôi đã thực hiện theo cách này và nó hoạt động tốt, nhưng tôi có thể biết tại sao bạn vượt qua các tham chiếu ('&') trong các vòng lặp không? Các boong được tạo ra chỉ là tốt mà không cần đi qua tài liệu tham khảo. – DBedrenko

2

Bạn không thể sử dụng điều này với enum class. Bạn phải sử dụng kiểu cũ enum.

Nếu bạn khăng khăng bạn sử dụng chúng. Tôi có thể đề nghị bạn một giải pháp xấu không sử dụng (trừ khi bạn sẽ không nói cho ai biết rằng tôi đã gợi ý nó):

for (Rank rank = Rank::one; 
    static_cast<int>(rank) < static_cast<int>(Rank::enum_count); 
    rank = static_cast<Rank>(static_cast<int>(rank)+1)){ 
}; 
+0

Cảm ơn bạn đã trả lời, bạn có thể cho tôi biết tại sao sử dụng 'static_cast' như thế này là" xấu "không? – DBedrenko

+0

không phải static_cast .. toàn bộ giải pháp là giả. Tôi hỏi một câu hỏi tương tự trước khi loại bỏ điều này và không có nhiều lựa chọn. Tôi nghĩ bạn nên xem xét lại toàn bộ thiết kế để tìm một cách tốt hơn. –

+0

Hmm thì có vấn đề gì với giải pháp này? Tôi đang tìm kiếm một thiết kế tốt hơn, nhưng một lớp Enum có vẻ là cách hoàn hảo để mô hình hóa bộ quần áo và xếp hạng thẻ ... – DBedrenko

3

Bạn có thể đúc suitrank bạn biến một int& và tăng chúng như vậy.

for (Suit suit = Suit::clubs; suit < Suit::enum_count; ((int&)suit)++) { 
     for (Rank rank = Rank::one; rank < Rank::enum_count; ((int&)rank)++) { 

Tuy nhiên, điều này có thể gây ra một số vấn đề như khi bạn gán giá trị cho mục nhập enum của mình.


Bạn cũng có thể tạo ra một chức năng nhỏ mà thực hiện điều này cho bạn với đúng loại:

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); 
     }; 
    }; 
}; 
+0

Cảm ơn các giải pháp thú vị. '(Int &)' làm gì ở đây chính xác? Tôi biết 'int & some_arg' có thể được sử dụng trong các định nghĩa tham số hàm để truyền qua tham chiếu. – DBedrenko

+1

'int &' là một tham chiếu đến một biến kiểu 'int'. Vì vậy, khi bạn thay đổi tham chiếu, bạn thay đổi biến giá trị tham chiếu tham chiếu. '((int &) phù hợp với) ++' cũng có thể được viết như 'int & temp = (int &)suit; temp ++;'. Lý do duy nhất này làm việc là 'Suit' và' Rank' "kế thừa" int, do đó dàn diễn viên này là –

+0

@Simon Làm cho nó trở nên phổ biến và bạn vàng. – LogicStuff

2

câu trả lời bổ sung để đáp ứng với old_mountain's answer:

Bạn có thể trong một số trường hợp ngăn chặn mà bạn quên để thêm giá trị mới vào danh sách của bạn bằng cách sử dụng mảng cố định.Vấn đề chính với điều này là initializer chấp nhận lập luận ít hơn quy định nhưng bạn có thể làm việc xung quanh này:

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)... } }; 
} 

Tôi tìm thấy nguồn cảm hứng cho make_array trong câu hỏi này, nhưng sửa đổi nó: How to emulate C array initialization "int arr[] = { e1, e2, e3, ... }" behaviour with std::array?

Những gì nó là sử dụng đối số đầu tiên để tìm ra loại std::array sẽ là số và số đối số để có kích thước thật. Vì vậy, kiểu trả về là std::array<firstType, numArgs>.

Bây giờ tuyên bố danh sách của bạn như thế này:

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); 

Khi bạn thêm một giá trị cho mảng của bạn enum_count hoặc bất cứ điều gì giá trị mà bạn đang sử dụng như delimiter sẽ thay đổi và do đó sự phân công từ make_array sẽ thất bại như các kích thước của cả hai loại std::array s khác nhau (kết quả là các loại khác nhau).

Ví dụ:

Nếu bạn chỉ cần thêm một mới Suit, chúng ta hãy nói hexa

enum class Suit : int { clubs, diamonds, hearts, spades, hexa, enum_count }; 

Trình biên dịch sẽ thất bại với:

cannot convert from 'std::array<T,0x04>' to 'const std::array<Suit,0x05>' 

Tôi phải thừa nhận rằng tôi không phải là 100% hài lòng với giải pháp này vì nó đòi hỏi một dàn diễn viên khá xấu xí để size_t trong khai báo mảng.

1

Tôi cũng muốn chia sẻ cách tiếp cận của tôi, dựa trên một previous answer of mine mà tạo ra map<enum,string> sử dụng C++ 11 và C++ 14 tính năng, mã này là một trong những dưới đây:

// 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 ...); 
} 

Với mẫu này, chúng tôi có thể thay đổi các nhà xây dựng Deck theo cách này:

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); 
     }; 
    }; 
}; 

yêu cầu duy nhất để thực hiện các công việc toàn bộ điều là để gọi initialize chức năng theo cách này:

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" 
); 

Bạn có thể xem qua số Live example.

Các vấn đề liên quan