连续枚举C ++ 11


17

有没有一种方法可以在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

1
意思是继续,它具有升序还是意味着从零开始,然后每个值+1?
RoQuOTriX

5
由于无法枚举枚举标签,因此无法从程序本身内部进行枚举。
一些程序员伙计

1
有趣。我在考虑模板编程的思路,以及如何使编译器计算阶乘的思路。您将使用两个边界A和C来开始事情,并且模板函数通过SFINAE检查两者中是否存在所有值enum。可悲的是我有一份日常工作,因此尽管我会基于这种方法提高答案,但无法尝试将其写出来。我很确定像@barry或@sehe这样的人可以做到。
Bathsheba

1
@RoQuOTriX您如何将值与标签匹配?您将如何检查标签的顺序?以及如何在编译时完成static_assert?即使您不能做出一个“美丽的解决方案”,也请写一个答案,因为我很好奇如何以一种通用的方式来完成它。
一些程序员伙计

1
@Someprogrammerdude您所描述的是“美丽”或好的解决方案。我的意思是“简单”的检查解决方案,您必须为每个枚举和上帝保佑重写该解决方案,我希望没有人这样做
RoQuOTriX

Answers:


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

请注意,确实如此,正如库名所暗示的那样,“魔术” –该库在许多编译器特有的技巧上起作用。因此,它并不能真正满足您对“纯C ++”的要求,但在我们拥有该语言的反射功能之前,它可能会达到我们想要的水平。


确实是魔术,但这将最适合我的情况。
巴特

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

显然,这专用于给定的枚举,但是可以使用预处理器自动定义此类功能。


如果我理解正确的话,这仍然需要在minmax的开关和列表中写出所有枚举值。目前,我有多个枚举,因此确实有可能,但对于我的情况不是首选。
巴特

1

我希望看到一个答案。我也一直需要它。

不幸的是,我认为使用现有实用程序是不可能的。如果要在此上实现类型特征,则需要编译器的支持,因此为其编写模板听起来不可行。

我已经用特定标签扩展了枚举,以指示它是连续的,并立即为您提供了大小:枚举类构造函数c ++,如何传递特定值?

或者,您可以编写自己的特征:

 template<T> struct IsContiguous : std::false_type {};

每当您定义要使用的连续枚举时,都需要对此进行专门化处理。不幸的是,如果枚举被更改,则需要一些维护和注意。


1
您可以编写代码检查器,以检查类型是否设置正确
RoQuOTriX

确实是的。如果您有能力编写它。
JVApen

1

所有枚举都是连续的。始终允许为0;允许的最大值是向上舍入到下一个1<<N -1(所有位均为1)的最高枚举数,并且中间的所有值也都被允许。([dcl.enum] 9.7.1 / 5)。如果定义了负的枚举数,则通过舍入最低的枚举数类似地定义允许的最小值。

中定义的枚举数enum是具有范围内的值和正确类型的常量表达式,但是您可以定义之外的enum具有相同属性的其他常量:

constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)


2
尽管您是正确的,但从OP中可以明显看出,我们希望了解定义的值。(PS:否决票不是我的)
JVApen

1
@JVApen:那是确切的问题。“定义的值”不是枚举类型本身的属性。该标准明确规定了枚举的值。
MSalters
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.