【问题标题】:strongly enum to integer and vice-versa强枚举到整数,反之亦然
【发布时间】:2013-09-09 11:33:34
【问题描述】:

这是我的来源(作为答案),如何实现underlying_value 和to_enum 函数。 How to automatically convert strongly typed enum into int?

underlying_value - 没问题。 但是,to_enum - 有问题。

见:

enum class E{ a = 1, b = 3, c = 5 };
auto e_a = utils::underlying_value(E::a); //OK
E t = utils::to_enum<E>( 2 ) ; // compiled, but it's incorrect. I think here must throws exception?

Q:如何正确实现to_enum?

【问题讨论】:

  • 枚举具有与其基础类型相同的范围,只是某些值具有特殊名称。 E(2) 是一个非常好的值。
  • 我知道这条规则。但是 t 不等于 E:: 中的任何一个。如何检查我,value 是否等于 E:: 之一或不在 to_enum 内?
  • 这并没有使它不正确,至少就语言而言。您的抱怨是,t 不对应于任何E枚举器。如果没有某种形式的反射,就无法检查这一点,而标准 C++ 中不存在这种反射。
  • 我的意思是logically incorrect。我认为为了正确转换,需要检查可能性值。它对大型项目很有价值,程序员很容易忘记检查可能性值。
  • @KhurshidNormuradov 它不能在 C++ 中完成。

标签: c++ c++11 enums strongly-typed-enum


【解决方案1】:

尽管问题上有 cmets,但这可以在 C++11 中完成,但要在不重复代码的情况下完成,您最终必须将 enum class 声明包装在宏中。根据您的需要,这可能会使我的答案不合适。无论哪种方式,进行检查转换都需要一些机器,所以我将最后介绍宏。

基本思路是使用constexpr函数扫描一个数组:

#include <iostream>
#include <stdexcept>

enum class E { a = 1, b = 3, c = 5 };

constexpr E         values[] = {E::a, E::b, E::c};
constexpr size_t    count = sizeof(values) / sizeof(E);

constexpr E to_enum(int value, size_t index = 0)
{
    return
        index >= count ? throw std::runtime_error("invalid integer") :
        static_cast<int>(values[index]) == value ? values[index] :
        to_enum(value, index + 1);
}

constexpr E         converted = to_enum(3);

// Will not compile if uncommented.
// constexpr E         bad_converted = to_enum(2);

int main()
{
    std::cout << static_cast<int>(converted) << std::endl;

    return 0;
}

这打印出3。如果bad_converted 的行未注释,则此代码根本不会编译,正如它所说的。检查的转换可以在运行时或编译期间完成。如果to_enum 的参数是编译时常量,它将在编译期间完成。此外,正如您可能看到的那样,它对values 进行了线性扫描,但如果它成为一个非常大的枚举的性能问题,则可以用另一种算法替换。


我刚刚展示的代码是一个显示底层方法的草图。为了使使用起来不那么痛苦,您应该将E 的声明包装在一个宏中,该宏将自动生成values[] 数组和相关函数。我将一次一点地展示和合理化这个宏的内容。

基本宏如下所示

// Declaration header
#define ENUM_CLASS(TypeName, __VA_ARGS__)

// Use
ENUM_CLASS(E, a = 1, b = 3, c = 5);

因此,在本例中,__VA_ARGS__ 将是标记 a = 1, b = 3, c = 5。因此,我们可以在宏中声明枚举本身,如下所示:

enum class TypeName { __VA_ARGS__ };

但是,我们不能简单地声明:

constexpr TypeName values[] = { __VA_ARGS__ };

因为它扩展为

constexpr TypeName values[] = { a = 1, b = 3, c = 5 };

它没有作用域(每个值前面缺少TypeName::),并且由于数组初始值设定项中的额外赋值运算符而不是有效的C++。我先解决第二个问题。你需要定义一个像这样的类:

template <typename E>
class swallow_assignment {
  public:
    E _value;

    constexpr explicit swallow_assignment(E value) : _value(value)
    {
    }

    template <typename Any>
    constexpr const swallow_assignment& operator =(Any other) const
    {
        return *this;
    }

    constexpr operator E() const
    {
        return _value;
    }
};

现在,您可以写(swallow_assignment&lt;E&gt;)E::a = 1。将会发生的是,在编译时,E::a 将被转换为可分配值(swallow_assignment&lt;E&gt;)E::a,其内部表示与E::a 相同。然后该值将忽略1 的分配,然后将转换回E::a


剩下的就是给每个声明的常量加上前缀,这样我们就可以得到

constexpr TypeName values[] =
    {(swallow_assignment<E>)E::a = 1,
     (swallow_assignment<E>)E::b = 3,
     (swallow_assignment<E>)E::c = 5})

现在将是一个有效的初始化程序。这可以通过映射宏来完成。我不会在这里详细介绍,因为那是一个完全独立的主题,但是可以在https://github.com/aantron/better-enums/blob/e28177b11a9e3d7152c5216d84fdf8939aff0eba/enum_preprocessor_map.h 找到这样的宏。 Boost也可能有更好的。无论您使用什么宏,我都会假设它的签名是PP_MAP(prefix, __VA_ARGS__)。整个枚举的最终宏定义草图变为:

#define ENUM_CLASS(TypeName, __VA_ARGS__)                  \
    enum class TypeName { __VA_ARGS__ };                   \
    constexpr TypeName values[] =                          \
        { PP_MAP((swallow_assignment<TypeName>)TypeName::, \
                 __VA_ARGS__) };                           \
    constexpr size_t count = sizeof(values) / sizeof(TypeName);

您可能希望将这些定义填充到特征类型的特化中,以便您可以将此宏与多个enum class 一起使用(否则名为values 的数组将发生冲突)。但是,如果您将 values 设为特征类的静态成员,您可能必须使用弱符号来避免链接问题。

这些最后几点留作练习,因为这个答案已经太长了:) 我有一个库可以完成上述所有工作,尽管它包装了 enum 而不是为 @987654353 提供特征特化@。但是,有一个未发布的分支与 enum class/traits 的组合。您可以在此处查看该库:http://aantron.github.io/better-enums。该库的 ::_from_integral() 方法对应于您问题中的 to_enum 函数,它同时进行运行时和编译时转换。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 2015-11-18
    • 1970-01-01
    • 2014-11-08
    相关资源
    最近更新 更多