简体   繁体   English

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

[英]How to iterate over enumerators of an enum class?

Is there a way to initialize a container (eg std::unordered_set<char> ) with the enumerators of an enum class ?有没有办法用枚举enum class的枚举器初始化容器(例如std::unordered_set<char> )?

I have this class:我有这个 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 << ' ';
    }
}

But I want the chars_for_drawing set to be initialized with the enumerators:但我希望用枚举器初始化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 << ' ';
    }
}

As can be seen, the second approach is a bit messy.可以看出,第二种方法有点混乱。 Is there way to iterate over the enumerators and assign them to the unordered_set ?有没有办法遍历枚举器并将它们分配给unordered_set Maybe by using a lambda?也许通过使用 lambda?

No there is no straightforward way.不,没有直接的方法。 Something one often forgets: The range of the enums values is determined by its underlying type.人们经常忘记的一件事:枚举值的范围由其基础类型决定。 The enumerators are just some named constants.枚举器只是一些命名常量。 Concerning iterating the enumerators your enum:关于迭代枚举器你的枚举:

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

helps for iterating as much as a有助于进行尽可能多的迭代

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

does: Not at all.确实:一点也不。

Things are different when the enumerators have consecutive values and a hack that is used sometimes is to use a special enumerator to denote the "size":当枚举器具有连续的值并且有时使用的技巧是使用特殊的枚举器来表示“大小”时,情况就不同了:

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

That alone is a little silly, because the mapping to the actual characters is lost.仅此一点就有点傻,因为到实际字符的映射丢失了。 However, it can be supplied by an array:但是,它可以由数组提供:

#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 Reconsider if an enum is the right tool for the job. TL;DR 重新考虑枚举是否是适合该工作的工具。 Enums are often overestimated for what they can really do.枚举通常被高估了它们真正能做什么。 Sometimes not an enum is the better alternative.有时不是枚举是更好的选择。

In C++, the enum and enum class do not provide the kind of functionality you want.在 C++ 中, enumenum class不提供您想要的那种功能。

You can implement that functionality yourself.您可以自己实现该功能。 There is considerable boilerplate, but it may be worth it since it will make the callsite considerably easier to use.有相当多的样板,但它可能是值得的,因为它会使调用站点更容易使用。

Depending on the enumeration definitions of an enum or enum class , the implementation routines can accommodate sequences with gaps.根据enumenum class的枚举定义,实现例程可以适应带有间隙的序列。

Here is an example, with a simple enum class , that has no gaps.这是一个示例,使用简单的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, dolor, sit, amet, consectetur, adipiscing
};

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&)");
    e = static_cast<Lorem>(+e + 1);
    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";
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM