【问题标题】:Continuous enum C++11连续枚举 C++11
【发布时间】:2020-05-22 10:20:13
【问题描述】:

如果枚举是continuous,有没有办法在 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?
  • 无法枚举枚举标签,因此无法从程序内部进行。
  • 有趣。我正在按照模板编程的思路思考如何让编译器计算阶乘。您将使用两个边界 A 和 C 开始,模板函数通过 SFINAE 检查 enum 中它们之间的所有值是否存在。可悲的是,我有一份日常工作,因此无法尝试将其写出来,尽管我会根据这种方法投票赞成答案。我很确定像 @barry 或 @sehe 这样的人可以做到。
  • @RoQuOTriX 您如何将值与标签匹配?您将如何检查标签的顺序?以及如何在编译时完成(static_assert 需要)?即使您无法做出“漂亮的解决方案”,也请无论如何写一个答案,因为我很好奇如何以通用方式完成。
  • @Someprogrammerdude 您所描述的是“漂亮”或好的解决方案。我的意思是“简单”的检查解决方案,你必须为每个枚举重写它,上帝保佑,我希望没有人这样做

标签: c++ c++11 c++14 c++17 c++20


【解决方案1】:

对于许多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;
    }
}

请注意,正如库名称所暗示的那样,这确实是“魔法”——库函数在许多特定于编译器的 hack 上运行。因此,它并不能真正满足您对“纯 C++”的要求,但在我们拥有该语言的反射工具之前,它可能已经达到了我们所能达到的水平。

【讨论】:

    【解决方案2】:

    这在纯 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。目前我有多个枚举,所以这确实是可能的,但不适合我的情况。
    【解决方案3】:

    所有枚举都是连续的。总是允许 0;允许的最大值是向上舍入到下一个1&lt;&lt;N -1 的最高枚举数(所有位为一),并且也允许介于两者之间的所有值。 ([dcl.enum] 9.7.1/5)。如果定义了负枚举数,则允许的最小值同样通过向下舍入最小枚举数来定义。

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

    constexpr enum class Types_Discontinuous = static_cast&lt;Types_Discontinuous&gt;(2)

    【讨论】:

    • 尽管您是对的,但从 OP 中可以清楚地看出,我们想知道定义值的这一点。 (PS:反对票不是我的)
    • @JVApen:这正是问题所在。 “定义的值”不是枚举类型本身的属性。该标准明确说明了枚举的值是什么。
    【解决方案4】:

    我很想看到这个问题的答案。我也一直需要它。

    不幸的是,我认为使用现有实用程序无法做到这一点。如果您想在此基础上实现类型特征,则需要编译器的支持,因此为其编写模板听起来并不可行。

    我已经使用特定标签扩展了枚举以表明它是连续的,并立即为您提供大小:enum class constructor c++ , how to pass specific value?

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

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

    每当您定义要在其中使用它的连续枚举时,都需要对其进行专门化。不幸的是,如果枚举发生变化,这需要一些维护和注意。

    【讨论】:

    • 你可以编写一个代码检查器,在编译时检查类型是否正确
    • 是的,确实如此。如果你有能力写。
    猜你喜欢
    • 2023-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-03-30
    相关资源
    最近更新 更多