连续枚举 C++11

Bar*_*art 17 c++ c++11 c++14 c++17 c++20

如果枚举是连续的,有没有办法在 C++11 中检查?

给出不是的枚举值是完全有效的。是否有像 C++14、C++17 或 C++20 中的类型特征这样的特性来检查枚举是否连续?这将在 static_assert 中使用。

一个小例子如下:

enum class Types_Discontinuous {
  A = 10,
  B = 1,
  C = 100
};

enum class Types_Continuous {
  A = 0,
  B = 1,
  C = 2
};

static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous");    // Passes
Run Code Online (Sandbox Code Playgroud)

And*_*hev 7

这在纯 C++ 中是不可能的,因为无法枚举枚举值,或发现值的数量以及最小值和最大值。但是您可以尝试使用编译器的帮助来实现接近您想要的东西。例如,在 gcc 中,如果switch语句不处理枚举的所有值,则可能会强制执行编译错误:

enum class my_enum {
    A = 0,
    B = 1,
    C = 2
};

#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif

constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
    // Check that we know all enum values. Effectively works as a static assert.
    switch (t)
    {
    // Intentionally no default case.
    // The compiler will give an error if not all enum values are listed below.
    case my_enum::A:
    case my_enum::B:
    case my_enum::C:
        break;
    }

    // Check that the enum is continuous
    auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
    return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}

#pragma GCC diagnostic pop
Run Code Online (Sandbox Code Playgroud)

显然,这是专门针对给定的枚举,但可以使用预处理器自动定义此类函数。


N. *_*ead 7

对于许多enums,您可能可以使用Magic Enum库来解决这个问题。例如:

#include "magic_enum.hpp"

template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
    // make sure we're actually testing an enum
    if constexpr (!std::is_enum_v<Enum>)
        return false;
    else {
        // get a sorted list of values in the enum
        const auto values = magic_enum::enum_values<Enum>();
        if (std::size(values) == 0)
            return true;

        // for every value, either it's the same as the last one or it's one larger
        auto prev = values[0];
        for (auto x : values) {
            auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
            if (x != prev && x != next)
                return false;
            else
                prev = x;
        }
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,正如库名所暗示的那样,这确实是“魔术”——库函数在许多特定于编译器的黑客中。因此,它并不能真正满足您对“纯 C++”的要求,但在语言中具有反射功能之前,它可能与我们所能达到的一样好。