簡體   English   中英

如何在C ++中從枚舉轉換為類型(並在模板中使用)?

[英]How to do a conversion from enum to type (and use as it in a template) in C++?

這是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);

_

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

基於注釋的一些澄清:1)classify.h是公共API的一部分(對於第三方/其他公司),因此,在編譯時無法識別類型。 然而,為EAllowedTypes中的所有類型編譯模板沒有問題。 EAllowedTypes主要用於與客戶端進行通信,並允許將來更改內部數據類型(無需更改公共API),因此無法保證一對一映射。 2)實體不是A,B,BC等中任何一個的基類。它只是一個通用的對象,我們想要將它與A,B或C分類相關聯.A,B等的實現細節。附加應該保持隱藏。

您可以執行以下操作:

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

每次要添加新的可能類時cl<eX, X>都需要添加新的cl<eX, X> 這基本上是一個“遞歸” switch ,可能會被編譯器優化為簡單的switch 1 這里的想法是使用cl結構將enum值與其類鏈接起來。

如果你想在外面存儲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>{}>{});
}

然后你只需要修改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... ");
}

有沒有辦法,你需要的是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()

只需刪掉枚舉。 枚舉代​​表數字。 類型是類型。 為什么混合這兩個? 您只能對類型和模板執行相同的操作!

首先,讓我們將允許類型的枚舉更改為類型列表,這可能更適合您的問題:

using AllowedTypes = std::tuple<TypeA, TypeB, TypeC>;

之后,不要將函數classifyEntityAs作為數字發送,而是發送一個類型:

template<typename T>
bool classifyEntityAs(const Entity& ent) { /* ... */ }

現在,將類型發送到classify函數非常簡單:

template<typename T>
bool classifyEntityAs(const Entity& ent) {
    classify<T>(ent);
}

如果你想限制你可以發送給函數的類型,我們將需要一些元編程 ,它將包括比較類型以檢查類型T是否是類型列表之一。

template<typename, typename>
struct is_one_of_list;

// Case where the list is empty. T cannot match to an empty list
template<typename T>
struct is_one_of_list<T, std::tuple<>> : std::false_type {};

// Case where the list is at least one.
// We match the type T with Head.
// If T is not the same as Head, match with Tail list using is_one_of_list
template<typename T, typename Head, typename... Tail>
struct is_one_of_list<T, std::tuple<Head, Tail...>> : std::integral_constant<bool,
    std::is_same<T, Head>::value || is_one_of_list<T, std::tuple<Tail...>>::value
> {};

好的,現在困難的部分已經完成。 我們可以使用我們制作的新類型特征限制您的功能:

template<typename T, std::enable_if_t<is_one_of_list<T, AllowedTypes>::value>* = nullptr>
bool classifyEntityAs(const Entity& ent) { /* ... */ }

調用函數時,只允許使用AllowedTypes類型。

您可以查看Coliru上的實例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM