如何遍历枚举类的枚举器?

How to iterate over enumerators of an enum class?

提问人:digito_evo 提问时间:1/26/2022 最后编辑:digito_evo 更新时间:1/27/2022 访问量:315

问:

有没有办法用 的枚举器初始化容器(例如 )?std::unordered_set<char>enum 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 << ' ';
    }
}

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

C++ 初始化 无序集 枚举类

评论

1赞 463035818_is_not_an_ai 1/26/2022
枚举在我们希望它们成为的样子和它们实际;)之间存在着巨大的差距。是否可以选择为枚举器提供连续值(并以不同的方式向字符提供映射)?
0赞 digito_evo 1/26/2022
@463035818_is_not_a_number我不明白。你能解释一下吗?
0赞 n. m. could be an AI 1/26/2022
“有没有办法迭代”没有。
1赞 Eljay 1/26/2022
enum class Foo { bar, quux, baz };然后。现在,您可以使用 Foo enumdefs 的 FooX 进行迭代。vector<Foo> FooX{Foo::bar, Foo::quux, Foo::baz};

答:

1赞 463035818_is_not_an_ai 1/26/2022 #1

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

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

有助于迭代多达

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

does:一点也不。

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

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:重新考虑枚举是否是适合这项工作的工具。枚举经常被高估到它们真正能做什么。有时,不使用枚举是更好的选择。

评论

0赞 digito_evo 1/27/2022
我的枚举类的可见性如何?我是否应该将其设为私有,因为没有其他类会访问它?
0赞 463035818_is_not_an_ai 1/27/2022
@digito_evo,如果没有人会访问它,为什么首先要使用枚举呢?对不起,我不明白这个问题(什么班级?
0赞 digito_evo 1/27/2022
我的意思是AllowedChars
0赞 463035818_is_not_an_ai 1/27/2022
@digito_evo如果你使用枚举是因为你认为它有助于迭代,那么不要使用它,它没有帮助。如果枚举是出于其他原因,那么这就是正交的。将不需要公开的东西设为私有是很好的做法,尽管我不太确定你的意思,因为他们不是某个班级的成员
0赞 digito_evo 1/27/2022
枚举类是 的成员。Foo
1赞 Eljay 1/26/2022 #2

在 C++ 中,和不提供所需的功能。enumenum class

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

根据 or 的枚举定义,实现例程可以容纳有间隙的序列。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";
    }
}

评论

0赞 463035818_is_not_an_ai 1/26/2022
这很好,但 OP 的情况是关于枚举器不是连续的