열거 형을 어떻게 반복 할 수 있습니까?
방금 ++ 또는 + =와 같은 열거 형에서 표준 수학 연산자를 사용할 수 없다는 것을 알았습니다.
그렇다면 C ++ 열거 형의 모든 값을 반복하는 가장 좋은 방법은 무엇입니까?
일반적인 방법은 다음과 같습니다.
enum Foo {
One,
Two,
Three,
Last
};
for ( int fooInt = One; fooInt != Last; fooInt++ )
{
Foo foo = static_cast<Foo>(fooInt);
// ...
}
열거 형 Last
은 반복에 의해 건너 뛰기위한 것입니다. 이 "가짜" Last
열거 형을 사용하면 새 열거 형을 추가 할 때마다 for 루프에서 종료 조건을 마지막 "실제"열거 형으로 업데이트 할 필요가 없습니다. 나중에 더 많은 열거 형을 추가하려면 마지막 전에 추가하십시오. 이 예제의 루프는 여전히 작동합니다.
물론 열거 형 값을 지정하면 다음과 같이 분류됩니다.
enum Foo {
One = 1,
Two = 9,
Three = 4,
Last
};
이것은 열거 형이 실제로 반복되지 않는다는 것을 보여줍니다. 열거 형을 처리하는 일반적인 방법은 스위치 명령문에서 열거 형을 사용하는 것입니다.
switch ( foo )
{
case One:
// ..
break;
case Two: // intentional fall-through
case Three:
// ..
break;
case Four:
// ..
break;
default:
assert( ! "Invalid Foo enum value" );
break;
}
실제로 열거하려면 열거 형 값을 벡터에 채우고 반복하십시오. 지정된 열거 형 값도 올바르게 처리합니다.
#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;
}
열거 형이 0으로 시작하고 증가량이 항상 1 인 경우
enum enumType
{
A = 0,
B,
C,
enumTypeEnd
};
for(int i=0; i<enumTypeEnd; i++)
{
enumType eCurrent = (enumType) i;
}
그렇지 않다면 유일한 이유는
vector<enumType> vEnums;
항목을 추가하고 일반 반복자를 사용하십시오 ....
c ++ 11을 사용하면 실제로 간단한 템플릿 화 된 사용자 정의 반복자를 작성하는 대안이 있습니다.
열거 형이 있다고 가정 해 봅시다
enum class foo {
one,
two,
three
};
이 일반 코드는 매우 효율적으로 트릭을 수행합니다. 일반 헤더에 배치하면 반복해야 할 열거 형에 대해 서비스를 제공합니다.
#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;
그런 다음 range-for를 사용하여 반복 할 수 있습니다
for (foo i : fooIterator() ) { //notice the parentheses!
do_stuff(i);
}
열거 형에 공백이 없다는 가정은 여전히 사실입니다. 열거 형 값을 저장하는 데 실제로 필요한 비트 수에 대한 가정은 없습니다 (std :: underlying_type 덕분)
이 솔루션이 너무 복잡합니다.
enum NodePosition { Primary = 0, Secondary = 1, Tertiary = 2, Quaternary = 3};
const NodePosition NodePositionVector[] = { Primary, Secondary, Tertiary, Quaternary };
for (NodePosition pos : NodePositionVector) {
...
}
열거 형으로는 할 수 없습니다. 아마도 열거 형이 상황에 가장 적합하지 않을 수도 있습니다.
일반적인 규칙은 MAX와 같은 마지막 열거 형 값의 이름을 지정하고 int를 사용하여 루프를 제어하는 데 사용하는 것입니다.
나는 종종 그런 식으로
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 EAnimal
{
E_First,
E_None = E_First,
E_CanFly = 0x1,
E_CanWalk = 0x2
E_CanSwim = 0x4,
E_Last
}
for (EAnimali = E_First; i < E_Last; i = EAnimal(i << 1))
{}
다음 매크로를 시도하고 정의 할 수 있습니다.
#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;
}
부호없는 정수, 열거 형 및 문자를 통해 앞뒤로 반복하는 데 사용할 수 있습니다.
for_range (unsigned, i, 10,0)
{
cout << "backwards i: " << i << endl;
}
for_range (char, c, 'z','a')
{
cout << c << endl;
}
어색한 정의에도 불구하고 매우 잘 최적화되어 있습니다. VC ++에서 디스어셈블러를 보았습니다. 코드는 매우 효율적입니다. 끝내지 말고 세 개의 for 문을 사용하십시오. 컴파일러는 최적화 후 하나의 루프 만 생성합니다! 닫힌 루프를 정의 할 수도 있습니다.
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;
}
공백이있는 열거 된 유형을 반복 할 수는 없습니다.
다른 답변에서 다루지 않은 것 = 강력한 형식의 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)
}
열거 된 유형의 증분 / 감소 연산자를 오버로드 할 수도 있습니다.
스위치에서 열거 형을 사용하면 컴파일러에서 누락 된 사례 COUNT를 경고하기 때문에 최종 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);
}
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);
}
참고 : 이것은 간단한 템플릿 사용자 정의 반복자 답변보다 훨씬 적은 코드입니다.
이 typeof
대신 GCC를 사용 하여이 작업을 수행 할 수는 decltype
있지만 현재 컴파일 할 수있는 컴파일러는 없습니다.
연속 열거 형에만 작동하는 또 다른 솔루션이 있습니다. 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))
{
// ...
}
enum에 순차적으로 번호가 매겨 졌다고 가정하면 오류가 발생하기 쉽습니다. 또한 선택된 열거 자에 대해서만 반복 할 수 있습니다. 해당 하위 집합이 작은 경우 명시 적으로 반복하면 우아한 선택이 될 수 있습니다.
enum Item { Man, Wolf, Goat, Cabbage }; // or enum class
for (auto item : {Wolf, Goat, Cabbage}) { // or Item::Wolf, ...
// ...
}
C ++에는 내부 검사가 없으므로 런타임에 이러한 종류의 것을 결정할 수 없습니다.
열거 형 값이 순차적이라는 것을 알고 있다면 (예 : Qt : Key 열거 형) 다음을 수행 할 수 있습니다.
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);
}
}
예상대로 작동합니다.
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 with -O3
는 비 순차적 값이 2 개이거나 순차적 인 값이 상당히 많은 경우 위 배열을 최적화합니다 (최대 6 테스트). 그러나 if
진술 이있는 경우에만이 작업을 수행 합니다. (순차 배열의 모든 요소보다 큰 정수 값을 비교 한 명령문을 시도했지만 제외되지 않았음에도 반복을 인라인했지만 if 문을 생략하면 값이 메모리에 저장되었습니다.) 하나의 비 순차 열거 형의 값 https://godbolt.org/z/XuGtoc] . 이 이상한 행동은 캐시 및 분기 예측과 관련된 깊은 휴리스틱으로 인한 것으로 생각됩니다.
다음은 배열이 항상 인스턴스화되지는 않음을 보여주는 godbolt의 간단한 테스트 반복에 대한 링크 입니다.
이 기술의 가격은 열거 형 요소를 두 번 쓰고 두 목록을 동기화 상태로 유지합니다.
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]
}
정수 배열을 만들고 배열을 반복하지만 마지막 요소를 -1로 만들고 종료 조건에 사용하십시오.
열거 형이 다음과 같은 경우 :
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] );
}
-1 검사가 물론 요소 중 하나와 충돌하지 않는 한 표현의 정수에 관계없이 분해되지 않습니다.
참고 URL : https://stackoverflow.com/questions/261963/how-can-i-iterate-over-an-enum
'IT' 카테고리의 다른 글
시스템 오버레이 창 만들기 (항상 상단) (0) | 2020.03.24 |
---|---|
body-parser는 express와 어떤 관계가 있습니까? (0) | 2020.03.24 |
MySQL VARCHAR 최대 크기는 얼마입니까? (0) | 2020.03.24 |
미디어 쿼리는 화면 대신 div 요소를 기반으로 크기를 조정할 수 있습니까? (0) | 2020.03.24 |
Spring Security를 사용할 때 Bean에서 현재 사용자 이름 (예 : SecurityContext) 정보를 얻는 올바른 방법은 무엇입니까? (0) | 2020.03.24 |