如何在C++中从枚举转换为类型(并在模板中使用)?

mr_*_*r_T 8 c++ enums templates c++11

这是C#中可能的(参见如何将枚举转换为类型),但我想知道如何在C++ 11中实现它?

可能的代码框架(也显示我的预期用途)将是:

// classify.h (part of public API) 
// definitions of classes A, B, C cannot be exposed, only a list of names
// (some names might even refer to the same type of object)

enum EAllowedTypes { eA, eB, eC };
class Entity;

bool classifyEntityAs(const Entity& ent, EAllowedTypes type);
Run Code Online (Sandbox Code Playgroud)

_

// classify.cpp
#include "A.h"  // defines class A (derived from a common base class)
#include "BC.h" // defines class BC (derived from a common base class)

template <typename T> 
bool classify(const Entity &) 
{ 
    /* some implementation, with possible specializations */
}

// how to implement this kind of vector/map from enum(int) to existing classes
static const std::vector enumToType</*ClassType*/> =
{
    /* eA */ class A,    
    /* eB */ class BC,
    /* eC */ class BC
};

bool classifyEntityAs(cont Entity& ent, EAllowedTypes type)
{
    return classify<enumToType[type]>(ent);
}
Run Code Online (Sandbox Code Playgroud)

基于注释的一些澄清:1)classify.h是公共API的一部分(对于第三方/其他公司),因此,在编译时无法识别类型.然而,为EAllowedTypes中的所有类型编译模板没有问题.EAllowedTypes主要用于与客户端进行通信,并允许将来更改内部数据类型(无需更改公共API),因此无法保证一对一映射.2)实体不是A,B,BC等中任何一个的基类.它只是一个通用的对象,我们想要将它与A,B或C分类相关联.A,B等的实现细节.附加应该保持隐藏.

Hol*_*olt 5

您可以执行以下操作:

template <EAllowedTypes e, typename T>
struct cl {
    static const decltype(e) value = e;
    using type = T;
};

template <typename...>
struct classifier;

template <>
struct classifier<> {
    static bool classify(const Entity& ent, EAllowedTypes type) {
        throw std::logic_error("Whatever... ");
    }
};

template <typename T, typename... Args>
struct classifier<T, Args...> {
    static bool classify(const Entity& ent, EAllowedTypes type) {
        return type == T::value ? 
            ::classify<typename T::type>(ent)
            : classifier<Args...>::classify(ent, type);
    }
};

bool classifyEntityAs(const Entity& ent, EAllowedTypes type) {
    return classifier<cl<eA, A>, cl<eB, BC>, cl<eC, BC>>::classify(ent, type);
}
Run Code Online (Sandbox Code Playgroud)

这将要求您在cl<eX, X>每次要添加新的可能类时添加一个新类。这基本上是一个“递归” switch,可能会被编译器优化为简单的switch1。这里的想法是enum使用cl结构将值与其类联系起来。

如果要存储cl外部列表,可以执行以下操作:

using enumToType = std::tuple<cl<eA, A>, cl<eB, BC>, cl<eC, BC>>;

template <size_t... Idx>
bool classifyEntityAs(const Entity& ent, EAllowedTypes type, 
                      std::index_sequence<Idx...>) {
    return classifier<std::tuple_element_t<Idx, enumToType>...>::classify(ent, type);
}

bool classifyEntityAs(const Entity& ent, EAllowedTypes type) {
    return classifyEntityAs(ent, type, 
        std::make_index_sequence<std::tuple_size<enumToType>{}>{});
}
Run Code Online (Sandbox Code Playgroud)

然后你只需要修改enumToType(我在std::tuple这里使用,但你可以有一个只包含类型的自定义结构)的定义。


1这段代码基本上等价于:

switch (type) {
  case eA: return classify<A>(ent);
  case eB: return classify<BC>(ent);
  case eC: return classify<BC>(ent);
  default: throw std::logic_error("Whatever... ");
}
Run Code Online (Sandbox Code Playgroud)


W.F*_*.F. 3

没有办法直接按照你的意愿去做,因为 c++ 不支持反射机制,而且我认为它至少在一段时间内不会改变......但是......如果你知道你的类型应该支持哪些类型分类器,您可以根据您的用例场景调整以下代码:

#include <map>
#include <memory>
#include <iostream>


enum EAllowedTypes { eA, eB, eC };

struct A {
  void run() {
    std::cout << "A::run() invoked" << std::endl;
  }
};
struct B {
  void run() {
    std::cout << "B::run() invoked" << std::endl;
  }
};
struct C {
  void run() {
    std::cout << "C::run() invoked" << std::endl;
  }
};

struct ITypeWrapper {
   virtual void run() = 0;
};

template <class T>
struct TypeWrapper: ITypeWrapper {
   void run() {
      T t;
      t.run();
   }
};

template <EAllowedTypes AT, class Type>
struct Mapper { };

template <class... Mappers>
struct Classifier;

template <EAllowedTypes... ATs, class... Types>
struct Classifier<Mapper<ATs, Types>...> {
   static std::map<EAllowedTypes, std::shared_ptr<ITypeWrapper>> m;
};

template <EAllowedTypes... ATs, class... Types>
std::map<EAllowedTypes, std::shared_ptr<ITypeWrapper>> Classifier<Mapper<ATs, Types>...>::m = { { ATs, std::make_shared<TypeWrapper<Types>>() }... };

using ABCClassifier = Classifier<Mapper<eA, A>, Mapper<eB, B>, Mapper<eC, C>>;

int main() {
   ABCClassifier::m[eA]->run();
}
Run Code Online (Sandbox Code Playgroud)

输出:

A::run() 被调用