使用可变参数模板参数构建枚举

时间:2013-09-25 04:45:15

标签: c++ c++11

我有一个相当简单的变体类,它支持一组预定义的类型,并提供一个枚举来指示哪些可用类型当前是活动的。像这样:

class variant
{ 
  enum class type { integer, real, string, etc };
  type active_type() const;
  /* ... */ 
};

我想把这个类变成一个模板,其中支持的类型作为模板参数提供:

template <typename... T>
class variant
{ 
  const std::type_info& active_type() const; // logical, but can't switch on it
  /* ... */ 
};

我依赖于捕获错误的一个关键特性是我可以在活动类型上switch,如果错过了任何可能的情况,编译器将发出警告。使用上述设计是不可能的(也不能使用boost::variant)。

我的问题是,有没有办法让我自动生成一个枚举数与参数包中参数数量相同的枚举?

枚举的实际名称/值无关紧要,因为它们可以隐藏在用于将类型映射到正确枚举的constexpr函数后面。我可以想象这样的最终用法:

template <typename... T>
class variant
{
  enum class type { T... }; // magic here

  // specializations provided to map T into type (for use in case labels)
  template <typename T>
  static constexpr type type_enum();

  type active_type() const;
  /* ... */
};

typedef variant<int, float, std::string> myvar;
myvar var;
switch (var.active_type())
{
case myvar::type_enum<int>(): // static constexpr function
  ...
  break;
case myvar::type_enum<float>():
  ...
  break;
} // warning: enumeration for string not handled in switch

1 个答案:

答案 0 :(得分:3)

这个问题已经有两年了,但由于没有答案,我在其他地方找不到解决方案,这就是我解决问题的方法。

第一个明显的问题是具有匹配参数数量的枚举类型(让编译器进行切换检查)。我看到的唯一方法是一系列模板专业化。

template <typename Tag, size_t size> struct Enum;
template <typename Tag> struct Enum<Tag, 0> {
    enum class Type { };
};
template <typename Tag> struct Enum<Tag, 1> {
    enum class Type {
        VALUE0
    };
};
template <typename Tag> struct Enum<Tag, 2> {
    enum class Type {
        VALUE0, VALUE1
    };
};
template <typename Tag> struct Enum<Tag, 3> {
    enum class Type {
        VALUE0, VALUE1, VALUE2
    };
};
template <typename Tag> struct Enum<Tag, 4> {
    enum class Type {
        VALUE0, VALUE1, VALUE2, VALUE3
    };
};

是的,它需要一些手动输入,但它不是一个大问题,请记住,类型的数量通常是有限的,并且可以根据需要添加新的特化(即方法是静态安全的) 。 标记参数用于区分具有相同数量值的枚举。

其次我们需要一些递归模板魔术来枚举类型:

template <typename EnumType, typename Type, typename... Types> struct TypeInfo;
template <typename EnumType, typename Type, typename... Types> struct TypeInfo<EnumType, Type, Type, Types...> {
    static const EnumType value = EnumType::VALUE0;
};
template <typename EnumType, typename Type, typename FirstType, typename... Types> struct TypeInfo<EnumType, Type, FirstType, Types...> {
    static const EnumType value = static_cast<EnumType>(static_cast<typename std::underlying_type<EnumType>::type>(TypeInfo<EnumType, Type, Types...>::value) + 1);
};

最后一个将所有部分组合在一起的课程:

template <typename Tag, typename... Ts> class TypedEnum {
    private:
        struct InternalTag;

    public:
        static const size_t NUM_TYPES = sizeof...(Ts);
        typedef typename Enum<InternalTag, NUM_TYPES>::Type Type;

        template <typename T> static constexpr decltype(TypeInfo<Type, T, Ts...>::value) getValue() { // SFINAE guard
            return TypeInfo<Type, T, Ts...>::value;
        }
};

瞧!你可以切换! (用铿锵声测试)

struct Tag0;
struct Tag1;
typedef TypedEnum<Tag0, int, float, char> Enum0;
typedef TypedEnum<Tag0, bool, float, char> Enum1; // Incompatible with Enum0!

Enum0::Type tpy = Enum0::getValue<char>(); // 2
switch(tpy) {
    case Enum0::getValue<int>():
        break;
    case Enum0::getValue<float>():
        break;
    case Enum0::getValue<char>():
        break;
}

功能摘要:

  • 静态拒绝不在枚举中的类型
  • 静态警告缺少开关案例
  • 包装中不同标签和/或参数的不兼容枚举类型

缺少功能:

  • 需要一些模板魔法来拒绝带有重复项的参数包