2011-12-14 34 views
47

Tôi có một đoạn mã lặp lại nơi tôi lặp lại tất cả các thành viên của một số enum class.Cho phép dựa trên phạm vi Đối với các lớp học enum?

Vòng lặp for mà tôi hiện đang sử dụng trông rất khó sử dụng so với range-based for mới.

Có cách nào để tận dụng lợi thế của các tính năng C++ 11 mới để cắt giảm độ dài cho vòng lặp for hiện tại của tôi không?

Mã hiện tại mà tôi muốn cải thiện:

enum class COLOR 
{ 
    Blue, 
    Red, 
    Green, 
    Purple, 
    First=Blue, 
    Last=Purple 
}; 

inline COLOR operator++(COLOR& x) { return x = (COLOR)(((int)(x) + 1)); } 

int main(int argc, char** argv) 
{ 
    // any way to improve the next line with range-based for? 
    for(COLOR c=COLOR::First; c!=COLOR::Last; ++c) 
    { 
    // do work 
    } 
    return 0; 
} 

Nói cách khác, nó sẽ được tốt đẹp nếu tôi có thể làm điều gì đó như:

for(const auto& c : COLOR) 
{ 
    // do work 
} 
+9

Thú vị. Ada có tính năng này từ năm 1983. – Shark8

+7

'(COLOR) (((int) (x) + 1))' Thay vì 'int', hãy xem xét sử dụng' std :: bases_type :: type'. –

+5

Dự kiến ​​rằng Purple bị bỏ qua? – kennytm

Trả lời

25

enumerations lặp với kiểu liệt kê chính nó như là một iterator là một ý tưởng tồi, và tôi khuyên bạn nên sử dụng một iterator thực tế như trong câu trả lời của deft_code. Nhưng nếu điều này thực sự là những gì bạn muốn:

COLOR operator++(COLOR& x) { return x = (COLOR)(std::underlying_type<COLOR>::type(x) + 1); } 
COLOR operator*(COLOR c) {return c;} 
COLOR begin(COLOR r) {return COLOR::First;} 
COLOR end(COLOR r) {COLOR l=COLOR::Last; return l++;} 

int main() { 
    for(const auto& c : COLOR()) { //note I added parenthesis here to make an instance 
     //do work 
    } 
    return 0; 
} 

Làm việc ở đây: http://ideone.com/cyTGD8


Về phía iterator của sự vật, cách dễ nhất chỉ đơn giản là:

extern const COLOR COLORS[(int)COLOR::Last+1]; 
const COLOR COLORS[] = {COLOR::Blue, COLOR::Red, COLOR::Green, COLOR::Purple}; 

int main() { 
    for(const auto& c : COLOR()) { //note I added parenthesis here to make an instance 
     //do work 
    } 
    return 0; 
} 

Như đã thấy ở đây: http://ideone.com/9XadVt

(Việc khai báo và định nghĩa riêng biệt của mảng làm cho nó trở thành một lỗi trình biên dịch nếu số lượng màu không khớp với số phần tử trong mảng. ety séc)

+2

Cần một toán tử '* để làm cho' COLOR' trở thành một trình lặp đầu vào. –

+0

@ R.MartinhoFernandes chữ ký của toán tử '* này trông như thế nào? – kfmfe04

+2

@ kfmfe04 Tôi đã thêm nó vào câu trả lời. –

2

Dưới đây là một ví dụ thử nghiệm (GCC 4.6.1):.

enum class COLOR 
{ 
    Blue, 
    Red, 
    Green, 
    Purple, 
    First=Blue, 
    Last=Purple 
}; 

COLOR operator++(COLOR& x) { return x = (COLOR)(((int)(x) + 1)); } 

COLOR operator*(COLOR c) {return c;} 

COLOR begin(COLOR r) {return COLOR::First;} 
// end iterator needs to return one past the end! 
COLOR end(COLOR r) {return COLOR(int(COLOR::Last) + 1);} 


int main() 
{ 
    for (const auto& color : COLOR()) std::cout << int(color); //
    return 0; 
} 
+1

Điều này cũng làm việc với Intel C++ 2013 SP1 trên Linux, khi bạn cài đặt Bản cập nhật 1 này không biên dịch được, chúng tôi đã báo cáo sự cố với Intel Engineering cho điều đó. –

38

Cá nhân tôi không thích quá tải toán tử ++ cho enums. Thường thì tăng thêm giá trị enum không thực sự hợp lý. Tất cả những gì thực sự muốn là một cách để lặp qua enum.

Dưới đây là một lớp chung chung Enum hỗ trợ lặp lại. Đó là chức năng nhưng không đầy đủ. Việc triển khai thực sự sẽ làm tốt để hạn chế quyền truy cập vào hàm tạo và thêm tất cả các đặc điểm của trình lặp.

#include <iostream> 

template< typename T > 
class Enum 
{ 
public: 
    class Iterator 
    { 
    public: 
     Iterator(int value) : 
     m_value(value) 
     { } 

     T operator*(void) const 
     { 
     return (T)m_value; 
     } 

     void operator++(void) 
     { 
     ++m_value; 
     } 

     bool operator!=(Iterator rhs) 
     { 
     return m_value != rhs.m_value; 
     } 

    private: 
     int m_value; 
    }; 

}; 

template< typename T > 
typename Enum<T>::Iterator begin(Enum<T>) 
{ 
    return typename Enum<T>::Iterator((int)T::First); 
} 

template< typename T > 
typename Enum<T>::Iterator end(Enum<T>) 
{ 
    return typename Enum<T>::Iterator(((int)T::Last) + 1); 
} 

enum class Color 
{ 
    Red, 
    Green, 
    Blue, 
    First = Red, 
    Last = Blue 
}; 

int main() 
{ 
    for(auto e: Enum<Color>()) 
    { 
     std::cout << ((int)e) << std::endl; 
    } 
} 
+1

Đồng ý. Điều này là nhiều, tốt hơn nhiều so với các toán tử quá tải trên chính kiểu liệt kê đó. –

+0

+1 Nice - Tôi đã sửa đổi lớp Enum của riêng tôi để theo định dạng của bạn (không phải trong OP). Kiểu an toàn là tốt đẹp, nhưng 'T from_string (const string & T)' là một chút đau như trong C++, chúng ta không thể quá tải trên giá trị enum trả về. Cùng một vấn đề tồn tại có hay không tôi sử dụng một mẫu-Enum, nhưng với mẫu-Enum, nó chỉ là một chút tiết hơn. – kfmfe04

+6

[Làm việc hoàn thành triển khai] (http://stacked-crooked.com/view?id=bcee5da83cd5c0738a17962bc00ee82d), cũng lưu ý rằng tôi đã thay đổi giá trị của 'Last' để phù hợp hơn với các phạm vi lặp bình thường. –

4

Bạn có thể có thể làm điều gì đó thông minh với boost :: mpl, một phiên bản thô có thể trông giống như:

#include <typeinfo> 

// ---------------------------------------------------------------------------| 
// Boost MPL 
// ---------------------------------------------------------------------------| 
#include <boost/mpl/for_each.hpp> 
#include <boost/mpl/iterator_range.hpp> 
#include <boost/mpl/range_c.hpp> 

namespace mpl = boost::mpl; 

using namespace std; 

enum class COLOR 
{ 
    Blue, 
    Red, 
    Green, 
    Purple, 
    Last 
}; 

struct enumValPrinter 
{ 
    template< typename T > 
    void operator() (const T&) 
    { 
     cout << "enumValPrinter with: " << typeid(T).name() << " : " 
      << T::value << "\n"; 
    } 
}; 

int main(int, char**) 
{ 
    typedef mpl::range_c< int, static_cast<int>(COLOR::Blue), 
          static_cast<int>(COLOR::Last) > Colors; 
    mpl::for_each<Colors>(enumValPrinter()); 
    return 0; 
} 
1

Tôi thích ý tưởng rất nhiều và thường muốn cho nó.

Vấn đề tôi thấy là những gì sẽ xảy ra khi có giá trị số lặp lại cho một mục enum. Tất cả các triển khai mà tôi thấy ở trên yêu cầu phôi để loại không thể tách rời và ++. Cuối cùng, tôi nghĩ rằng hỗ trợ ngôn ngữ có thể được yêu cầu để thực sự lặp qua từng mục trong mọi trường hợp. Nó sẽ loại bỏ sự cần thiết phải có đầu tiên, cuối cùng hoặc bắt đầu, kết thúc mặc dù tôi không phản đối điều này quá nhiều. Nó giống như tìm kiếm start() end() cho container.

enum class COLOR 
{ 
    Blue, 
    Red, 
    Green, 
    Mauve = 0, 
    Purple, 
    Last 
}; 

Việc đánh số bắt đầu tại Mauve.

+0

Có hoặc khi có khoảng trống. –

28
enum class Color { 
    blue, 
    red, 
    green = 5, 
    purple 
}; 
const std::array<Color,4> all_colors = {Color::blue, Color::red, Color::green, Color::purple}; 

Sau đó:

for (Color c : all_colors) { 
    //... 
} 

Nhiều lần tôi sử dụng nó như thế này, nơi tôi muốn có một 'none' giá trị:

// Color of a piece on a chess board 
enum class Color { 
    white, 
    black, 
    none 
}; 
const std::array<Color,3> colors = {Color::white, Color::black}; 

template <typename CONTAINER> 
bool has_item (CONTAINER const & c, typename CONTAINER::const_reference v) { 
    return std::find(c.begin(), c.end(), v) != c.end(); 
} 

bool is_valid (Color c) { 
    return has_item(colors, c) || c == Color::none; 
} 

bool do_it (Color c) { 
    assert(has_item(colors, c)); // here I want a real color, not none 
    // ... 
} 

bool stop_it (Color c) { 
    assert(is_valid(c));   // but here I just want something valid 
    // ... 
} 
+6

Đây là một câu trả lời tuyệt vời! Sự dư thừa của việc có các mục ở hai nơi là một điều đáng tiếc, nhưng nó là một giải pháp sạch hơn nhiều so với các giải pháp khác; điều này không có đúc, và khi bạn đề cập đến các giá trị không phải bắt đầu từ số không hoặc tiếp giáp. Bạn cũng có thể dễ dàng chỉ định tập con của các giá trị, ví dụ: darkColors và lightColors. Nó dựa một chút vào các enums có một số lượng khá nhỏ các giá trị, nhưng chúng thường làm. –

+0

Phụ lục cho câu trả lời của bạn: với giải pháp này bạn có thể nhận được tổng số giá trị với colors.size(). Nếu bạn cần một hằng số thời gian biên dịch (cho kích thước của một mảng khác, ví dụ) bạn có thể sử dụng std :: tuple_size (decltype (colors)) :: giá trị, đó là thừa nhận một chút dài quanh co nhưng hoàn toàn an toàn. –

+0

Tại sao kích thước của mảng được chỉ định là '3'? Không phải là '2'? Và nếu có, điều này có minh họa rằng việc vi phạm nguyên tắc DRY luôn dẫn đến những lỗi khó xem? –

0

Nếu bạn là một người khủng khiếp bạn có thể nhận hành vi này với bộ tiền xử lý, ví dụ như:

#include <vector> 
#include <cstdio> 

#define ENUM_NAME COLOR 
#define ENUM_VALUES \ 
    ENUM_VALUE(Blue) \ 
    ENUM_VALUE(Red) \ 
    ENUM_VALUE(Green) \ 
    ENUM_VALUE(Purple) 

// This block would be a #include "make_iterable_enum.h" 
#define ENUM_VALUE(v) v, 
enum class ENUM_NAME {ENUM_VALUES}; 
#undef ENUM_VALUE 
#define ENUM_VALUE(v) ENUM_NAME::v, 
#define VECTOR_NAME(v) values_ ## v 
#define EXPAND_TO_VECTOR_NAME(v) VECTOR_NAME(v) 
const std::vector<ENUM_NAME> EXPAND_TO_VECTOR_NAME(ENUM_NAME){ENUM_VALUES}; 
#undef ENUM_VALUE 
#undef ENUM_NAME 
#undef ENUM_VALUES 
#undef VECTOR_NAME 
#undef EXPAND_TO_VECTOR_NAME 
// end #included block 

int main() { 
    for (auto v : COLOR_values) { 
     printf("%d\n", (int)v); 
    } 
} 

Với những sửa đổi nhỏ, điều này cũng có thể hỗ trợ ví dụ. ENUM_SETVALUE (Xanh lam, 4) và tạo một bản đồ const từ ví dụ. COLOR :: Xanh lam thành "Xanh dương". Và ngược lại.

Tôi muốn tiêu chuẩn vừa xây dựng các tính năng này dưới dạng tùy chọn cho lớp enum. Không có giải pháp nào tốt.

0

Tôi chắc chắn rằng bạn có thể lặp qua các thành viên của một C++ initializer_list, vì vậy tôi nghĩ tôi đã làm điều này trong quá khứ:

enum class Color {Red, Green, Blue}; 

for (const Color c : {Color::Red, Color::Green, Color::Blue}) 
{ 
} 

Cho dù có những vấn đề với điều này, tôi không biết, nhưng tôi nghĩ rằng tôi muốn đề nghị nó vì nó là súc tích, nhưng không lý tưởng nếu có rất nhiều Màu sắc.

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