2011-12-14 10 views
47

मेरे पास कोड का आवर्ती हिस्सा है जहां मैं enum class के सभी सदस्यों पर लूप करता हूं।रेंज-आधारित के लिए एनम कक्षाओं के लिए अनुमति दें?

for लूप जो मैं वर्तमान में उपयोग करता हूं, नए range-based for की तुलना में बहुत अवांछित दिखता है।

क्या मेरे वर्तमान for लूप के लिए वर्बोजिटी पर कटौती करने के लिए नई सी ++ 11 सुविधाओं का लाभ उठाने का कोई तरीका है?

वर्तमान कोड है कि मैं सुधार करने के लिए करना चाहते हैं:

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

दूसरे शब्दों में, यह अच्छा होगा अगर मैं की तरह कुछ कर सकता है: एक के रूप में शुमार के साथ ही

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

दिलचस्प। अडा के पास 1 9 83 से यह सुविधा थी। – Shark8

+7

'(रंग) (((int) (x) + 1))' int' के बजाय, 'std :: underlying_type :: type' का उपयोग करने पर विचार करें। –

+5

क्या यह उम्मीद है कि बैंगनी छोड़ा गया है? – kennytm

उत्तर

25

पुनरावृत्ति enumerations इटेटरेटर एक खराब विचार है, और मैं deft_code के उत्तर में एक वास्तविक इटरेटर का उपयोग करने की सलाह देता हूं। लेकिन अगर यह सच है क्या आप चाहते हैं:

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

यहाँ कार्य करना: http://ideone.com/cyTGD8


चीजों की इटरेटर तरफ, सबसे आसान तरीका है बस है:

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

के रूप में यहां देखी गई: http://ideone.com/9XadVt

(सरणी की अलग घोषणा और डिफिंटिनियन इसे एक कंपाइलर त्रुटि बनाता है यदि रंगों की संख्या सरणी में तत्वों की संख्या से मेल नहीं खाती है। उत्कृष्ट आसान सुरक्षित ety जांच)

+2

को 'रंग' इनपुट इनपुटर बनाने के लिए 'ऑपरेटर *' की आवश्यकता है। –

+0

@ आर। मार्टिनिन्हो फर्नांडीस इस 'ऑपरेटर *' के हस्ताक्षर को किस तरह दिखाना चाहिए? – kfmfe04

+2

@ kfmfe04 मैंने इसे उत्तर में जोड़ा। –

2

यहाँ एक परीक्षण किया उदाहरण है (जीसीसी 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

यह लिनक्स पर इंटेल सी ++ 2013 एसपी 1 के साथ भी काम करता है, जब आप अद्यतन 1 स्थापित करते हैं तो यह संकलित करने में विफल रहता है, हमने इसके लिए इंटेल इंजीनियरिंग को एक समस्या की सूचना दी। –

38

मैं व्यक्तिगत रूप से enums के लिए ++ ऑपरेटर ओवरलोडिंग पसंद नहीं है। अक्सर वृद्धि एक enum मूल्य वास्तव में समझ में नहीं आता है। जो वास्तव में चाहता था वह enum पर पुनरावृत्ति करने का एक तरीका है।

नीचे एक सामान्य Enum वर्ग है जो पुनरावृत्ति का समर्थन करता है। यह कार्यात्मक लेकिन अपूर्ण है। एक वास्तविक कार्यान्वयन कन्स्ट्रक्टर तक पहुंच प्रतिबंधित करने और सभी इटरेटर लक्षणों को जोड़ने के लिए अच्छा होगा।

#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

सहमत हुए। यह गणित प्रकार पर ऑपरेटरों को ओवरलोड करने से कहीं ज्यादा बेहतर है। –

+0

+1 अच्छा - मैंने आपके प्रारूप का पालन करने के लिए अपनी खुद की एनम कक्षा संशोधित की है (ओपी में नहीं)। टाइप-सुरक्षा अच्छी है, लेकिन टी से_स्ट्रिंग (कॉन्स स्ट्रिंग एंड टी) 'सी ++ में दर्द का थोड़ा सा है, हम रिटर्न एनम वैल्यू पर ओवरलोड नहीं कर सकते हैं। एक ही समस्या मौजूद है या नहीं, मैं टेम्पलेट-एनम का उपयोग करता हूं, लेकिन टेम्पलेट-एनम के साथ, यह थोड़ा और वर्बोज़ है। – kfmfe04

+6

[पूर्ण कार्यान्वयन कार्य करना] (http://stacked-crooked.com/view?id=bcee5da83cd5c0738a17962bc00ee82d), यह भी ध्यान दें कि मैंने 'अंतिम' के मान को सामान्य इटरेटर श्रेणियों के साथ अधिक संगत होने के लिए बदल दिया है। –

4

आप शायद कुछ बढ़ावा :: एमपीएल के साथ चालाक कर सकता है, एक किसी न किसी संस्करण देखने के लिए की तरह हो सकता है:

#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

मैं विचार बहुत पसंद है और अक्सर इसके लिए कामना की है।

समस्या जो मैं देखता हूं वह तब होता है जब एनम आइटम के लिए दोहराए गए संख्यात्मक मूल्य होते हैं। उपर्युक्त सभी कार्यान्वयनों को अभिन्न प्रकार और ++ के लिए आवश्यक है। आखिरकार, मुझे लगता है कि सभी मामलों में प्रत्येक आइटम पर वास्तव में फिर से प्रयास करने के लिए भाषा समर्थन की आवश्यकता हो सकती है। यह पहली, आखिरी या शुरू करने की आवश्यकता को हटा देगा, अंत में हालांकि मैं इस पर बहुत अधिक विरोध नहीं करता हूं। यह कंटेनर के लिए शुरू() अंत() की तलाश में है।

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

माउव में संख्या शुरू हो रही है।

+0

हाँ या जब अंतराल हैं। –

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

तब:

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

कई बार मैं इसे इस तरह का उपयोग करें, जहां मैं एक 'कोई नहीं' मूल्य हैं:

// 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

यह एक अच्छा जवाब है! दो स्थानों में वस्तुओं को रखने की अनावश्यकता एक दयालु है, लेकिन यह अन्य समाधानों की तुलना में एक बहुत साफ समाधान है; इसमें कोई कास्टिंग नहीं है, और जैसा कि आप मानते हैं कि मान शून्य पर शुरू नहीं होते हैं या संगत नहीं होते हैं। आप मूल्यों के सबसेट आसानी से निर्दिष्ट कर सकते हैं उदा। अंधेरे रंग और प्रकाश रंग। यह बहुत कम मूल्य वाले मानकों पर थोड़ा सा निर्भर करता है, लेकिन वे आमतौर पर वैसे भी करते हैं। –

+0

आपके उत्तर में एक परिशिष्ट: इस समाधान के साथ आप colors.size() के साथ मानों की गिनती प्राप्त कर सकते हैं। यदि आपको एक संकलन समय निरंतर (उदाहरण के लिए, किसी अन्य सरणी के आकार के लिए) की आवश्यकता है, तो आप std :: tuple_size (decltype (colors)) :: मान का उपयोग कर सकते हैं, जो स्वीकार्य रूप से थोड़ा लंबा घुमाया गया है लेकिन पूरी तरह से सुरक्षित है। –

+0

सरणी का आकार '3' के रूप में निर्दिष्ट क्यों है? यह '2' नहीं होना चाहिए? और यदि हां, तो क्या यह स्पष्ट नहीं करता है कि DRY सिद्धांत का उल्लंघन करने से हमेशा कठोर दिखने वाली बग होती है? –

0

आप एक भयानक व्यक्ति को आप प्राप्त कर सकते हैं कर रहे हैं प्रीप्रोसेसर के साथ यह व्यवहार, कुछ:

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

मामूली संशोधन के साथ यह भी समर्थन कर सकता है उदाहरण के लिए। ENUM_SETVALUE (नीला, 4) और उदाहरण से एक कॉन्स मैप बनाना। रंग :: ब्लू टू ब्लू "। और इसके विपरीत।

मेरी इच्छा है कि मानक ने इन सुविधाओं को एनम कक्षा के विकल्प के रूप में बनाया है। कोई भी कामकाज अच्छा नहीं है।

0

मुझे यकीन है कि आप एक सी ++ initializer_list के सदस्यों से अधिक पुनरावृति कर सकते हैं हूँ, इसलिए मुझे लगता है मैं अतीत में इस किया है:

enum class Color {Red, Green, Blue}; 

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

चाहे वहाँ इस के साथ मुद्दों कर रहे हैं, मुझे नहीं पता पता है, लेकिन मैंने सोचा कि मैं इसे संक्षिप्त मानता हूं क्योंकि यह संक्षिप्त है, लेकिन बहुत सारे रंग होने पर आदर्श नहीं है।

संबंधित मुद्दे