如何迭代枚举 class 的枚举器?

How to iterate over enumerators of an enum class?

有没有办法用 enum classenumerators 来初始化容器(例如 std::unordered_set<char>)?

我有这个class:

#include <iostream>
#include <unordered_set>


class Foo
{
public:

    inline static const std::unordered_set<char> chars_for_drawing { '/', '\', '|', '-' };
};


int main( )
{
    for ( const char ch : Foo::chars_for_drawing )
    {
        std::cout << ch << ' ';
    }
}

但我希望 chars_for_drawing 集用枚举数初始化:

#include <iostream>
#include <unordered_set>


class Foo
{
public:
    enum class AllowedChars : char
    {
        ForwardSlash = '/',
        BackSlash = '\',
        VerticalSlash = '|',
        Dash = '-'
    };

    // inline static const std::unordered_set<char> chars_for_drawing { '/', '\', '|', '-' }; // not like this

    inline static const std::unordered_set<char> chars_for_drawing {
                                                                     static_cast<char>( AllowedChars::ForwardSlash ),
                                                                     static_cast<char>( AllowedChars::BackSlash ),
                                                                     static_cast<char>( AllowedChars::VerticalSlash ),
                                                                     static_cast<char>( AllowedChars::Dash )
                                                                    };
};


int main( )
{
    for ( const char ch : Foo::chars_for_drawing )
    {
        std::cout << ch << ' ';
    }
}

可以看出,第二种做法有点乱。有没有办法遍历枚举数并将它们分配给 unordered_set?也许通过使用 lambda?

没有直接的方法。人们经常忘记的一件事:枚举值的范围由其基础类型决定。枚举器只是一些命名常量。您的枚举:

enum class AllowedChars : char
{
    ForwardSlash = '/',
    BackSlash = '\',
    VerticalSlash = '|',
    Dash = '-'
};

有助于迭代 a

struct {
    char value;
    static const char ForwardSlash = '/';
    static const char BackSlash = '\';
    static const char VerticalSlash = '|';
    static const char Dash = '-';
};

会:完全不会。

当枚举数具有连续值时,情况就不同了,有时使用的 hack 是使用特殊的枚举数来表示“大小”:

enum class AllowedChars : char
{
    ForwardSlash,
    BackSlash,
    VerticalSlash,
    Dash,
    SIZE
};

int main() {
    for (int i=0;i< static_cast<int>(AllowedChars::SIZE); ++i){
        std::cout << i;
    }
}

仅此一点有点愚蠢,因为到实际字符的映射丢失了。但是,它可以由数组提供:

#include <iostream>

enum class AllowedCharNames : char
{
    ForwardSlash,
    BackSlash,
    VerticalSlash,
    Dash,
    SIZE
};

char AllowedChars[] = {'/','\','|','-'};

int main() {
    std::cout << AllowedChars[static_cast<size_t>(AllowedCharNames::Dash)];
    for (int i=0;i< static_cast<int>(AllowedCharNames::SIZE); ++i){
        std::cout << AllowedChars[i];
    }
}

TL;DR 重新考虑枚举是否适合这项工作。枚举的实际作用常常被高估。有时不是枚举是更好的选择。

在 C++ 中,enumenum class 不提供您想要的那种功能。

您可以自己实现该功能。有相当多的样板文件,但它可能是值得的,因为它会使调用站点更容易使用。

根据 enumenum class 的枚举定义,实现例程可以容纳有间隙的序列。

这是一个简单的例子 enum class,其中有间隙。

#include <iostream>
#include <stdexcept>
#include <utility>

using std::cout;
using std::logic_error;
using std::ostream;
using std::underlying_type_t;

namespace {


enum class Lorem {
    ipsum = 10, dolor = 20, sit = 30, amet = 100, consectetur = 105, adipiscing = 111
};

auto Lorem_first() -> Lorem { return Lorem::ipsum; }
auto Lorem_last() -> Lorem { return Lorem::adipiscing; }

auto operator<<(ostream& out, Lorem e) -> ostream& {
    switch(e) {
#define CASE(x) case Lorem::x: return out << #x
        CASE(ipsum);
        CASE(dolor);
        CASE(sit);
        CASE(amet);
        CASE(consectetur);
        CASE(adipiscing);
#undef CASE
    }

    throw logic_error("operator<< unknown Lorem");
}

auto operator+(Lorem e) -> underlying_type_t<decltype(e)> {
    return static_cast<underlying_type_t<decltype(e)>>(e);
}

auto operator++(Lorem& e) -> Lorem& {
    if (e == Lorem_last()) throw logic_error("operator++(Lorem&)");
    switch(e) {
#define CASE(x, y) case Lorem::x: e = Lorem::y; break
        CASE(ipsum, dolor);
        CASE(dolor, sit);
        CASE(sit, amet);
        CASE(amet, consectetur);
        CASE(consectetur, adipiscing);
#undef CASE
        case Lorem::adipiscing: break;
    }
    return e;
}

class Lorem_Range {
    bool done = false;
    Lorem iter = Lorem_first();

public:
    auto begin() const -> Lorem_Range const& { return *this; }
    auto end() const -> Lorem_Range const& { return *this; }
    auto operator*() const -> Lorem { return iter; }
    bool operator!=(Lorem_Range const&) const { return !done; }
    void operator++() {
        if (done) {
            throw logic_error("Lorem_Range::operator++");
        }

        if (iter == Lorem_last()) {
            done = true;
        } else {
            ++iter;
        }
    }
};

} // anon

int main() {
    for (auto e : Lorem_Range()) {
        cout << +e << ", " << e << "\n";
    }
}