ฉันจะวนซ้ำ enum ได้อย่างไร


304

ฉันเพิ่งสังเกตเห็นว่าคุณไม่สามารถใช้ตัวดำเนินการทางคณิตศาสตร์มาตรฐานกับ enum เช่น ++ หรือ + =

ดังนั้นวิธีที่ดีที่สุดในการวนซ้ำผ่านค่าทั้งหมดใน C ++ enum คืออะไร


2
หนึ่งในหลายวิธี: เมื่อ enum เพียงไม่เพียงพอ: เรียนการแจงนับสำหรับ C และหากคุณต้องการสิ่งที่ห่อหุ้มเพิ่มเติมลองวิธีนี้จาก James Kanze
Don Wakefield

รายการที่เชื่อมโยงมีคำตอบที่น่าสนใจ
โทนี่

คำตอบเหล่านี้ดูเหมือนจะไม่ครอบคลุมปัญหาที่intอาจไม่ใหญ่พอ! ( [C++03: 7.2/5])
การแข่งขัน Lightness ใน Orbit

ที่น่าสนใจคุณสามารถกำหนดoperator++enums; for(Enum_E e = (Enum_E)0; e < ENUM_COUNT; e++)แต่เพื่อให้คุณสามารถทำ หมายเหตุคุณต้องโยน0ไปEnum_Eเพราะ c ++ ห้ามผู้ประกอบการที่ได้รับมอบหมายใน enums
weberc2

หากมีตัวดำเนินการเวลาคอมไพล์คล้ายกับขนาดของงานที่สามารถเปล่ง std :: initializer_list ตามตัวอักษรประกอบด้วยค่าของ enum เราจะมีทางออกและจะไม่เกี่ยวข้องกับค่าใช้จ่ายรันไทม์ใด ๆ
jbruni

คำตอบ:


263

วิธีทั่วไปมีดังนี้:

enum Foo {
  One,
  Two,
  Three,
  Last
};

for ( int fooInt = One; fooInt != Last; fooInt++ )
{
   Foo foo = static_cast<Foo>(fooInt);
   // ...
}

โปรดทราบว่าค่า Enum Lastนั้นจะถูกข้ามโดยการทำซ้ำ การใช้Lastenum "ปลอม" นี้คุณไม่จำเป็นต้องอัปเดตเงื่อนไขการยกเลิกของคุณในการวนซ้ำไปยัง enum "ของจริง" ล่าสุดทุกครั้งที่คุณต้องการเพิ่ม enum ใหม่ หากคุณต้องการเพิ่ม enums เพิ่มเติมในภายหลังเพียงเพิ่มไว้ก่อนหน้า การวนซ้ำในตัวอย่างนี้จะยังคงใช้งานได้

แน่นอนนี้แบ่งลงถ้าระบุค่า enum:

enum Foo {
  One = 1,
  Two = 9,
  Three = 4,
  Last
};

นี่แสดงให้เห็นว่า enum ไม่ได้มีความตั้งใจที่จะทำซ้ำ วิธีทั่วไปในการจัดการกับ enum คือใช้ในคำสั่ง switch

switch ( foo )
{
    case One:
        // ..
        break;
    case Two:  // intentional fall-through
    case Three:
        // ..
        break;
    case Four:
        // ..
        break;
     default:
        assert( ! "Invalid Foo enum value" );
        break;
}

หากคุณต้องการแจกแจงให้ระบุค่า enum ในเวกเตอร์แล้ววนซ้ำไปเรื่อย ๆ สิ่งนี้จะจัดการกับค่า enum ที่ระบุเช่นกัน


13
โปรดทราบว่าในส่วนแรกของตัวอย่างถ้าคุณต้องการใช้ 'i' เป็น Foo enum และไม่ใช่ int คุณจะต้องใช้สแตติกแบบคงที่เช่น: static_cast <Foo> (i)
Clayton

5
นอกจากนี้คุณกำลังข้ามล่าสุดในวง ควรเป็น <= ล่าสุด
Tony

20
@Tony Last ถูกข้ามไป หากคุณต้องการเพิ่ม enums เพิ่มเติมในภายหลังให้เพิ่มก่อน Last ... การวนซ้ำในตัวอย่างแรกจะยังคงใช้งานได้ ด้วยการใช้ Enum ล่าสุด "ปลอม" คุณไม่จำเป็นต้องอัปเดตเงื่อนไขการยกเลิกของคุณในการวนซ้ำเป็น "จริง" ล่าสุดทุกครั้งที่คุณต้องการเพิ่ม Enum ใหม่
timidpueo

ยกเว้นตอนนี้คุณได้จัดสรรหน่วยความจำจริง ๆ แล้วเมื่อ enum หากเป็นหน่วยความจำที่มีการทำดัชนีและต่อเนื่องอย่างเข้มงวดสามารถทำงานได้โดยไม่ต้องจัดสรรหน่วยความจำ
Cloud

1
โปรดทราบว่าสำหรับความหมาย enum UNKNOWN = 0นี้เพื่อความปลอดภัยสำหรับการปรับปรุงหนึ่งควรกำหนดค่า นอกจากนี้ฉันขอแนะนำให้วางdefaultกรณีและปัญหาเมื่อสลับค่า enum เนื่องจากอาจซ่อนกรณีที่การจัดการค่าถูกลืมจนกระทั่งรันไทม์ แต่ควรเข้ารหัสค่าทั้งหมดและใช้UNKNOWNฟิลด์เพื่อตรวจสอบความเข้ากันไม่ได้
Benjamin Bannier

53
#include <iostream>
#include <algorithm>

namespace MyEnum
{
  enum Type
  {
    a = 100,
    b = 220,
    c = -1
  };

  static const Type All[] = { a, b, c };
}

void fun( const MyEnum::Type e )
{
  std::cout << e << std::endl;
}

int main()
{
  // all
  for ( const auto e : MyEnum::All )
    fun( e );

  // some
  for ( const auto e : { MyEnum::a, MyEnum::b } )
    fun( e );

  // all
  std::for_each( std::begin( MyEnum::All ), std::end( MyEnum::All ), fun );

  return 0;
}

ขอบคุณ! โปรดทราบว่าหากคุณกำลังข้ามไฟล์ / คลาสและหากความเข้ากันได้ของ MS ทำให้คุณมีปัญหากับค่าคงที่ที่ไม่ใช่จำนวนเต็มจะได้ช่วยให้คอมไพเลอร์ของฉันใส่ขนาดลงในส่วนหัวอย่างชัดเจน: static const Type All[3];จากนั้นฉันสามารถ เพื่อเริ่มต้นในแหล่งที่มา: const MyEnum::Type MyEnum::All[3] = { a, b, c }; ก่อนที่จะทำเช่นนั้นฉันได้รับError in range-based for...ข้อผิดพลาดที่น่าสะพรึงกลัว(เพราะอาร์เรย์มีขนาดที่ไม่รู้จัก) คิดออกนี้ขอบคุณคำตอบที่เกี่ยวข้อง
ปราชญ์

1
อาเรย์เวอร์ชันเป็นมิตรกับการคัดลอก คำตอบที่น่าพึงพอใจที่สุดนอกจากนี้ "ไม่" หรือ "สำหรับลำดับเท่านั้น" มาโครอาจเป็นมิตรได้
Paulo Neves

1
นี่อาจเป็นวิธีแก้ปัญหาที่ดีสำหรับ enums ที่มีจำนวนน้อย แต่สำหรับ enums ที่มีจำนวนมากนั้นจะต้องไม่พอดี
kato2

20

ถ้า enum ของคุณเริ่มต้นด้วย 0 และการเพิ่มขึ้นจะเป็น 1 เสมอ

enum enumType 
{ 
    A = 0,
    B,
    C,
    enumTypeEnd
};

for(int i=0; i<enumTypeEnd; i++)
{
   enumType eCurrent = (enumType) i;            
}

ถ้าไม่ใช่ฉันเดาว่าทำไมต้องสร้างบางอย่างเช่น

vector<enumType> vEnums;

เพิ่มรายการและใช้ตัววนซ้ำปกติ ....


19

ด้วย c ++ 11 จะมีทางเลือกอื่น: การเขียน iterator แบบกำหนดเองอย่างง่าย

สมมติว่า enum ของคุณคือ

enum class foo {
  one,
  two,
  three
};

รหัสทั่วไปนี้จะทำเคล็ดลับค่อนข้างมีประสิทธิภาพ - วางในส่วนหัวทั่วไปมันจะให้บริการคุณสำหรับ enum ใด ๆ ที่คุณอาจต้องทำซ้ำมากกว่า:

#include <type_traits>
template < typename C, C beginVal, C endVal>
class Iterator {
  typedef typename std::underlying_type<C>::type val_t;
  int val;
public:
  Iterator(const C & f) : val(static_cast<val_t>(f)) {}
  Iterator() : val(static_cast<val_t>(beginVal)) {}
  Iterator operator++() {
    ++val;
    return *this;
  }
  C operator*() { return static_cast<C>(val); }
  Iterator begin() { return *this; } //default ctor is good
  Iterator end() {
      static const Iterator endIter=++Iterator(endVal); // cache it
      return endIter;
  }
  bool operator!=(const Iterator& i) { return val != i.val; }
};

คุณจะต้องมีความเชี่ยวชาญ

typedef Iterator<foo, foo::one, foo::three> fooIterator;

จากนั้นคุณสามารถวนซ้ำโดยใช้ช่วงสำหรับ

for (foo i : fooIterator() ) { //notice the parentheses!
   do_stuff(i);
}

สมมติฐานที่ว่าคุณไม่มีช่องว่างใน enum ของคุณยังคงเป็นจริง ไม่มีข้อสันนิษฐานเกี่ยวกับจำนวนบิตที่จำเป็นจริง ๆ ในการจัดเก็บค่า enum (ขอบคุณ std :: รากฐาน _type)


1
@lepe คุณเพียงสร้าง typedef ที่แตกต่างกันสำหรับ enum อื่น
Andrew Lazarus

2
@lepe ของที่ชอบบอกว่าstd::vectorไม่ได้เพราะทั่วไปจะเชื่อมโยงกับstd::vector<foo> foo
Kyle Strand

1
typedef Iterator<color, color::green, color::red> colorIterator;ตรวจสอบให้แน่ใจว่าคุณเข้าใจการทำงานของอินสแตนซ์
Andrew Lazarus

2
โอ้ฉันเห็นปัญหา - ควรจะเป็นfoo operator*() { ... C operator*() { ...
Kyle Strand

1
@ KyleStrand: คุณเข้าใจแล้ว! ที่ทำให้รู้สึกโดยสิ้นเชิงในขณะนี้ ควรปรับปรุงรหัสหรือไม่ ขอบคุณทุกคนสำหรับคำอธิบายของคุณ
lepe

16

วิธีแก้ปัญหาที่ซับซ้อนเกินไปนี้ฉันชอบ:

enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};

const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };

for (NodePosition pos : NodePositionVector) {
...
}

ฉันไม่รู้ว่าทำไมสิ่งนี้จึงถูกลดระดับลง มันเป็นทางออกที่สมเหตุสมผล
Paul Brannan

11
ฉันคาดว่ามันเป็นเพราะรายการจะต้องมีการบำรุงรักษาในสองสถานที่
Ant

C ++ อนุญาตให้ใช้for (NodePosition pos : NodePositionVector)ไวยากรณ์หรือไม่ เท่าที่ฉันรู้ว่านี่คือไวยากรณ์ของ Java และคุณจะต้องมีตัววนซ้ำใน C ++ เพื่อทำสิ่งที่เทียบเท่า
thegreatjedi

3
@thegreatjedi ตั้งแต่ C ++ 11 คุณสามารถทำได้แม้กระทั่ง simpier: สำหรับ (auto pos: NodePositionVector) {.. }
Enzojz

@thegreatjedi การค้นหาหรือคอมไพล์โปรแกรมทดสอบเร็วกว่าการถามคำถาม แต่ใช่ตั้งแต่ C ++ 11 มันเป็นไวยากรณ์ C ++ ที่ถูกต้องสมบูรณ์ซึ่งคอมไพเลอร์แปลเป็นรหัสเทียบเท่า (และ verbose / บทคัดย่อที่น้อยกว่า) โค้ดมักจะผ่านตัววนซ้ำ; ดูcppreference และดังที่ Enzojz กล่าวว่า C ++ 11 ได้เพิ่มเข้ามาautoดังนั้นคุณไม่จำเป็นต้องประกาศประเภทขององค์ประกอบอย่างชัดเจนเว้นแต่คุณ (A) จำเป็นต้องใช้ตัวดำเนินการแปลงหรือ (B) ไม่ชอบautoด้วยเหตุผลบางประการ . forผู้ใช้ช่วงมากที่สุดใช้autoAFAICT
underscore_d

9

ฉันมักจะทำเช่นนั้น

    enum EMyEnum
    {
        E_First,
        E_Orange = E_First,
        E_Green,
        E_White,
        E_Blue,
        E_Last
    }

    for (EMyEnum i = E_First; i < E_Last; i = EMyEnum(i + 1))
    {}

หรือหากไม่ต่อเนื่อง แต่ใช้ขั้นตอนปกติ (เช่นค่าสถานะบิต)

    enum EAnimalCaps
    {
        E_First,
        E_None    = E_First,
        E_CanFly  = 0x1,
        E_CanWalk = 0x2
        E_CanSwim = 0x4,
        E_Last
    }

    class MyAnimal
    {
       EAnimalCaps m_Caps;
    }

    class Frog
    {
        Frog() : 
            m_Caps(EAnimalCaps(E_CanWalk | E_CanSwim))
        {}
    }

    for (EAnimalCaps= E_First; i < E_Last; i = EAnimalCaps(i << 1))
    {}

แต่การใช้การพิมพ์ค่า bit-wise คืออะไร
Anu

1
ใช้ enums เพื่อสร้าง bitmasks เช่นรวมหลายตัวเลือกในตัวแปรเดียวจากนั้นใช้ FOR เพื่อทดสอบสำหรับทุกตัวเลือก แก้ไขโพสต์ของฉันด้วยตัวอย่างที่ดีกว่า
Niki

ฉันยังไม่สามารถใช้มันได้ (& โพสต์ของคุณยังคงแสดงตัวอย่างเก่า)! การใช้ enum เป็น bitmasks มีประโยชน์จริง ๆ แต่ไม่สามารถเชื่อมต่อจุดต่าง ๆ ได้! คุณช่วยอธิบายรายละเอียดตัวอย่างในรายละเอียดหน่อยได้ไหมคุณสามารถใส่รหัสเพิ่มเติมได้เช่นกัน
Anu

@anu ขออภัยไม่เห็นความคิดเห็นของคุณ เพิ่มคลาส Frog เป็นตัวอย่าง bitmask
Niki

8

คุณไม่สามารถใช้ Enum ได้ บางที Enum อาจไม่เหมาะกับสถานการณ์ของคุณมากที่สุด

หลักการทั่วไปคือตั้งชื่อค่า enum ล่าสุดเช่น MAX และใช้เพื่อควบคุมการวนซ้ำโดยใช้ int


มีตัวอย่างมากมายที่แสดงให้เห็นถึงสิ่งที่ตรงกันข้าม ฉันเป็นคำแถลงของคุณเองที่คุณขัดแย้งกับตัวเอง (บรรทัดที่สอง)
Niki

6

สิ่งที่ไม่ได้รับการกล่าวถึงในคำตอบอื่น ๆ = หากคุณใช้ C ++ 11 พิมพ์จำนวนมากคุณจะไม่สามารถใช้งาน++หรือใช้+ intกับพวกเขาได้ ในกรณีนั้นจำเป็นต้องใช้เมสเซอร์ที่มีปัญหาเล็กน้อย:

enum class myenumtype {
  MYENUM_FIRST,
  MYENUM_OTHER,
  MYENUM_LAST
}

for(myenumtype myenum = myenumtype::MYENUM_FIRST;
    myenum != myenumtype::MYENUM_LAST;
    myenum = static_cast<myenumtype>(static_cast<int>(myenum) + 1)) {

  do_whatever(myenum)

}

3
... แต่ C ++ 11 แนะนำช่วงตามที่แสดงในคำตอบอื่น ๆ :-)
ปราชญ์

5

คุณสามารถลองและกำหนดแมโครต่อไปนี้:

#define for_range(_type, _param, _A1, _B1) for (bool _ok = true; _ok;)\
for (_type _start = _A1, _finish = _B1; _ok;)\
    for (int _step = 2*(((int)_finish)>(int)_start)-1;_ok;)\
         for (_type _param = _start; _ok ; \
 (_param != _finish ? \
           _param = static_cast<_type>(((int)_param)+_step) : _ok = false))

ตอนนี้คุณสามารถใช้มัน:

enum Count { zero, one, two, three }; 

    for_range (Count, c, zero, three)
    {
        cout << "forward: " << c << endl;
    }

มันสามารถใช้ในการวนซ้ำไปข้างหลังและไปข้างหน้าผ่านไม่ได้ลงนาม, จำนวนเต็ม, enums และตัวอักษร:

for_range (unsigned, i, 10,0)
{
    cout << "backwards i: " << i << endl;
}


for_range (char, c, 'z','a')
{
    cout << c << endl;
}

แม้จะมีคำจำกัดความที่ไม่เหมาะสม ฉันดู disassembler ใน VC ++ รหัสมีประสิทธิภาพอย่างมาก อย่าถูกเลื่อนออกไป แต่ข้อความทั้งสามสำหรับคำสั่ง: คอมไพเลอร์จะสร้างลูปเดียวหลังจากปรับให้เหมาะสม! คุณสามารถกำหนดลูปที่ล้อมรอบได้:

unsigned p[4][5];

for_range (Count, i, zero,three)
    for_range(unsigned int, j, 4, 0)
    {   
        p[i][j] = static_cast<unsigned>(i)+j;
    }

คุณไม่สามารถวนซ้ำตามประเภทที่ระบุด้วยช่องว่างได้อย่างชัดเจน


1
นั่นเป็นแฮ็คที่ยอดเยี่ยม! แม้ว่ามันจะเหมาะสมกว่าสำหรับ C มากกว่าสำหรับ C ++ แต่ก็อาจบอกว่า
einpoklum

3
_A1ไม่ใช่ชื่อที่อนุญาต แต่เป็นเครื่องหมายขีดเส้นใต้ที่มีตัวพิมพ์ใหญ่ต่อไปนี้
Martin Ueding

3

นอกจากนี้คุณยังสามารถโอเวอร์โหลดตัวดำเนินการเพิ่ม / ลดสำหรับประเภทที่ระบุของคุณ


1
คุณไม่สามารถโอเวอร์โหลดโอเปอเรเตอร์ใด ๆ ในประเภทที่ระบุด้วย C หรือ C ++ นอกจากว่าคุณจะสร้าง struct / class ที่เลียนแบบการแจกแจงค่า
เทรเวอร์ Hickey

2
C ++ อนุญาตให้ผู้ประกอบการโอเวอร์โหลดบน enums ดูstackoverflow.com/questions/2571456/...
Arch D. Robison

การบรรทุกมากขึ้นการเพิ่ม / ลดต้องใช้การตัดสินใจว่าจะทำอย่างไรเมื่อมีการล้น
Eponymous

3

การสันนิษฐานว่า enum นั้นถูกเรียงลำดับตามลำดับนั้นเกิดข้อผิดพลาดได้ง่าย นอกจากนี้คุณอาจต้องการวนซ้ำตัวแจงนับที่เลือกเท่านั้น หากเซ็ตย่อยนั้นมีขนาดเล็กวนซ้ำอย่างชัดเจนอาจเป็นทางเลือกที่หรูหรา:

enum Item { Man, Wolf, Goat, Cabbage }; // or enum class

for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
    // ...
}

นี่เป็นตัวเลือกที่ดีที่ฉันคิดว่า ต้องเป็นส่วนหนึ่งของข้อมูลจำเพาะ C ++ ที่ใหม่กว่าที่ฉันใช้เมื่อฉันถามคำถามที่ฉันคาดเดา
อดัม

ใช่. มันวนซ้ำ std :: initializer_list <Item> ลิงค์
marski

2

หากคุณไม่ชอบที่จะสร้างมลพิษให้คุณ enum ด้วยรายการ COUNT ขั้นสุดท้าย (เพราะบางทีถ้าคุณยังใช้ enum ในสวิตช์แล้วคอมไพเลอร์จะเตือนคุณเกี่ยวกับกรณีที่ขาดหายไป COUNT :) คุณสามารถทำสิ่งนี้ได้:

enum Colour {Red, Green, Blue};
const Colour LastColour = Blue;

Colour co(0);
while (true) {
  // do stuff with co
  // ...
  if (co == LastColour) break;
  co = Colour(co+1);
}

2

นี่เป็นอีกวิธีการหนึ่งที่ใช้งานได้สำหรับ enums ที่ต่อเนื่องกันเท่านั้น มันให้การคาดคะเนซ้ำยกเว้นความอัปลักษณ์ที่เพิ่มขึ้นซึ่งเป็นที่ที่มันเป็นเพราะนั่นคือสิ่งที่แตกใน C ++

enum Bar {
    One = 1,
    Two,
    Three,
    End_Bar // Marker for end of enum; 
};

for (Bar foo = One; foo < End_Bar; foo = Bar(foo + 1))
{
    // ...
}

1
การเพิ่มขึ้นสามารถทำให้สั้นลงfoo = Bar(foo + 1)ได้
HolyBlackCat

ขอบคุณ HolyBlackCat ฉันได้รวมข้อเสนอแนะที่ยอดเยี่ยมของคุณ! ฉันยังสังเกตเห็นว่า Riot มีวิธีแก้ปัญหาเดียวกันนี้ค่อนข้างมาก แต่สอดคล้องกับการพิมพ์ที่ดี (และทำให้ verbose มากขึ้น)
Ethan Bradford

2
enum class A {
    a0=0, a3=3, a4=4
};
constexpr std::array<A, 3> ALL_A {A::a0, A::a3, A::a4}; // constexpr is important here

for(A a: ALL_A) {
  if(a==A::a0 || a==A::a4) std::cout << static_cast<int>(a);
}

A constexpr std::arrayสามารถวนซ้ำได้แม้ไม่ต่อเนื่องกันโดยไม่ต้องมีอาร์เรย์ถูกสร้างอินสแตนซ์โดยคอมไพเลอร์ ขึ้นอยู่กับสิ่งต่าง ๆ เช่นการเพิ่มประสิทธิภาพของคอมไพเลอร์ฮิวริสติกและไม่ว่าคุณจะใช้ที่อยู่ของอาเรย์

ในการทดลองของฉันฉันพบว่าg++9.1 ด้วย-O3จะเพิ่มประสิทธิภาพอาร์เรย์ข้างต้นหากมีค่าที่ไม่ต่อเนื่อง 2 ค่าหรือค่าลำดับค่อนข้างน้อย (ฉันทดสอบถึง 6) แต่มันจะทำเช่นนี้หากคุณมีifคำสั่ง (ฉันลองคำสั่งที่เปรียบเทียบค่าจำนวนเต็มมากกว่าองค์ประกอบทั้งหมดในซีเควนเชียลอาร์เรย์และมันย้ำการทำซ้ำแม้ว่าจะไม่มีการแยก แต่เมื่อฉันออกจากคำสั่ง if ค่าถูกใส่ไว้ในหน่วยความจำ) ค่าจาก enum ที่ไม่ต่อเนื่องใน [หนึ่งกรณี | https://godbolt.org/z/XuGtoc] ฉันสงสัยว่าพฤติกรรมแปลก ๆ นี้เกิดจากการวิเคราะห์พฤติกรรมเชิงลึกเกี่ยวกับแคชและการทำนายสาขา

นี่คือลิงค์ไปยังการทดสอบซ้ำอย่างง่าย ๆ ใน godboltที่แสดงให้เห็นถึงอาร์เรย์ที่ไม่ได้รับการสร้างอินสแตนซ์เสมอไป

ราคาของเทคนิคนี้กำลังเขียนองค์ประกอบ enum สองครั้งและทำให้ทั้งสองรายการสอดคล้องกัน


ฉันชอบซีแมนทิกส์สำหรับช่วงลูปแบบง่าย ๆ และฉันคิดว่ามันจะมีวิวัฒนาการมากยิ่งขึ้นซึ่งเป็นสาเหตุที่ฉันชอบโซลูชันนี้
rtischer8277

2

ใน Bjarne Stroustrup ของการเขียนโปรแกรมภาษา C ++ หนังสือภาษาคุณสามารถอ่านที่เขาเสนอให้เกินพิกัดสำหรับคุณโดยเฉพาะoperator++ ประเภทที่ผู้ใช้กำหนดเองและโอเปอเรเตอร์การบรรทุกเกินพิกัดมีอยู่ในภาษาสำหรับสถานการณ์เฉพาะเหล่านี้enumenum

คุณจะสามารถรหัสต่อไปนี้:

#include <iostream>
enum class Colors{red, green, blue};
Colors& operator++(Colors &c, int)
{
     switch(c)
     {
           case Colors::red:
               return c=Colors::green;
           case Colors::green:
               return c=Colors::blue;
           case Colors::blue:
               return c=Colors::red; // managing overflow
           default:
               throw std::exception(); // or do anything else to manage the error...
     }
}

int main()
{
    Colors c = Colors::red;
    // casting in int just for convenience of output. 
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    std::cout << (int)c++ << std::endl;
    return 0;
}

รหัสทดสอบ: http://cpp.sh/357gb

enum classทราบว่าฉันใช้ รหัสทำงานได้ดีกับenumยัง แต่ฉันชอบenum classเนื่องจากมีการพิมพ์ที่แข็งแกร่งและสามารถป้องกันไม่ให้เราทำผิดพลาดในเวลารวบรวม


มีการโพสต์โหวตในโพสต์นี้ เหตุผลใดที่จะไม่ตอบคำถาม
LAL

เหตุผลอาจเป็นเพราะนี่เป็นวิธีการแก้ปัญหาที่เลวร้ายในเชิงสถาปัตยกรรม: มันบังคับให้คุณเขียนตรรกะที่มีความหมายระดับโลก - ไปผูกกับองค์ประกอบเฉพาะ (การแจงนับของคุณ) นอกจากนี้หากการแจงนับของคุณเปลี่ยนแปลงด้วยเหตุผลใดก็ตาม + ผู้ประกอบการด้วยวิธีการที่ไม่ยั่งยืนสำหรับโครงการขนาดกลางขนาดใหญ่ก็ไม่น่าแปลกใจที่มันมาจากคำแนะนำของ Bjarne Stroustrup ย้อนกลับไปในสมัยสถาปัตยกรรมซอฟต์แวร์เป็นเหมือนนิยายวิทยาศาสตร์
Manjia

enumคำถามเดิมเป็นเรื่องเกี่ยวกับการมีผู้ประกอบการไปยัง มันไม่ใช่คำถามสถาปัตยกรรม ฉันไม่เชื่อว่าในปี 2013 C ++ เป็นนิยายวิทยาศาสตร์
LAL

1

สำหรับคอมไพเลอร์ MS:

#define inc_enum(i) ((decltype(i)) ((int)i + 1))

enum enumtype { one, two, three, count};
for(enumtype i = one; i < count; i = inc_enum(i))
{ 
    dostuff(i); 
}

หมายเหตุ: นี่เป็นโค้ดที่น้อยกว่าคำตอบ iterator ที่กำหนดเองอย่างง่าย templatized

คุณสามารถทำให้มันใช้งานได้กับ GCC โดยใช้typeofแทนdecltypeแต่ฉันไม่มีคอมไพเลอร์ตัวนั้นในตอนนี้เพื่อให้แน่ใจว่าคอมไพล์แล้ว


สิ่งนี้ถูกเขียนขึ้น ~ 5 ปีหลังจากdecltypeกลายเป็นมาตรฐาน C ++ ดังนั้นคุณไม่ควรแนะนำสิ่งที่ล้าสมัยtypeofจาก GCC โบราณ GCC ล่าสุดของ Vaguely จัดการได้decltypeดี มีปัญหาอื่น ๆ : การปลดเปลื้องแบบ C หมดกำลังใจและมาโครยิ่งแย่ลง คุณสมบัติ C ++ ที่เหมาะสมสามารถให้ฟังก์ชั่นทั่วไปที่เหมือนกัน นี้จะถูกเขียนใหม่ดีกว่าการใช้งานฟังก์ชั่นแม่แบบ:static_cast และบรรยากาศที่ไม่จำเป็นสำหรับไม่ใช่template <typename T> auto inc_enum(T const t) { return static_cast<T>(static cast<int>(t) + 1); } enum classอีกวิธีหนึ่งตัวดำเนินการสามารถโอเวอร์โหลดต่อenumประเภท (TIL)
underscore_d

1
typedef enum{
    first = 2,
    second = 6,
    third = 17
}MyEnum;

static const int enumItems[] = {
    first,
    second,
    third
}

static const int EnumLength = sizeof(enumItems) / sizeof(int);

for(int i = 0; i < EnumLength; i++){
    //Do something with enumItems[i]
}

วิธีการแก้ปัญหานี้จะสร้างตัวแปรคงที่ที่ไม่จำเป็นในหน่วยความจำในขณะที่วัตถุประสงค์ของ enum เพียงเพื่อสร้าง 'หน้ากาก' ให้กับค่าคงที่แบบ
อินไลน์

นอกจากจะเปลี่ยนเป็นconstexpr static const int enumItems[]
Mohammad Kanan

0

C ++ ไม่มีวิปัสสนาดังนั้นคุณจึงไม่สามารถระบุสิ่งนี้ในเวลาทำงาน


1
คุณช่วยอธิบายให้ฉันฟังหน่อยได้ไหมว่าทำไม "การวิปัสสนา" จึงจำเป็นต้องทำซ้ำมากกว่า enum?
Jonathan Mee

บางทีคำนี้คือReflection ?
kͩeͣmͮpͥͩ

2
ฉันกำลังพยายามพูด 2 สิ่ง: 1) ตามคำตอบอื่น ๆ อีกมากมาย C ++ สามารถทำสิ่งนี้ให้สำเร็จดังนั้นหากคุณกำลังจะบอกว่าไม่สามารถทำได้จำเป็นต้องมีลิงค์หรือการชี้แจงเพิ่มเติม 2) ในรูปแบบปัจจุบันความคิดเห็นที่ดีที่สุดไม่ใช่คำตอบแน่นอน
Jonathan Mee

ลงคะแนนคำตอบของฉันแล้ว - ฉันคิดว่าคุณเป็นคนชอบธรรมมากกว่าที่เป็นจริง
kͩeͣmͮpͥͩ

1
ฉันจะอัดแน่นอีกครั้งใน 2 ความคิดเห็น: 1) ฉันไม่ downvote เพราะฉันพบว่าการได้รับการลดระดับการมีส่วนร่วมของเว็บไซต์ downvote ฉันพบว่าการต่อต้าน 2) ฉันยังไม่เข้าใจสิ่งที่คุณพยายามจะพูด แต่ดูเหมือนว่า คุณเข้าใจสิ่งที่ฉันไม่ได้ในกรณีนี้ฉันต้องการให้คุณทำอย่างละเอียดมากกว่าลบคำตอบ downvoted
Jonathan Mee

0

หากคุณรู้ว่าค่า enum นั้นเป็นลำดับตัวอย่างเช่น Qt: Key enum คุณสามารถ:

Qt::Key shortcut_key = Qt::Key_0;
for (int idx = 0; etc...) {
    ....
    if (shortcut_key <= Qt::Key_9) {
        fileMenu->addAction("abc", this, SLOT(onNewTab()),
                            QKeySequence(Qt::CTRL + shortcut_key));
        shortcut_key = (Qt::Key) (shortcut_key + 1);
    }
}

มันทำงานได้ตามที่คาดไว้


-1

เพียงแค่สร้างอาร์เรย์ของ ints และวนรอบอาร์เรย์ แต่ทำองค์ประกอบสุดท้ายที่บอกว่า -1 และใช้มันสำหรับเงื่อนไขการออก

ถ้า enum คือ:

enum MyEnumType{Hay=12,Grass=42,Beer=39};

จากนั้นสร้างอาร์เรย์:

int Array[] = {Hay,Grass,Beer,-1};

for (int h = 0; Array[h] != -1; h++){
  doStuff( (MyEnumType) Array[h] );
}

สิ่งนี้ไม่ทำลายลงไม่ว่า ints ในการแสดงตราบใดที่การตรวจสอบ -1 ไม่ชนกับหนึ่งในองค์ประกอบของหลักสูตร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.