从枚举值中获取正确的模板化类实例化

时间:2015-12-02 15:00:48

标签: c++ c++11

我有这个小问题,请看看:

#include <iostream>

enum class AnimalType
{
   bird,
   mammal,
   lizard,
   fish,
};

template<int LEGCOUNT, typename T>
class AbstractAnimal
{
public:
   T printLegCount() { std::cout << LEGCOUNT << std::endl; return 0; }
};

// The second parameter is NOT the leg type, it is just 
// a parameter to make each method signature unique
// as it is in my real source code
class Mammal : public AbstractAnimal<4, double>{};
class Bird : public AbstractAnimal<2, int>{};
class Lizard : public AbstractAnimal<4, float>{};
class Fish : public AbstractAnimal<0, long long>{};


void printLegCount(AnimalType animalType)
{
   switch (animalType)
   {
      case AnimalType::bird: Bird{}.printLegCount(); break;
      case AnimalType::mammal: Mammal{}.printLegCount(); break;
      case AnimalType::lizard: Lizard{}.printLegCount(); break;
      case AnimalType::fish: Fish{}.printLegCount(); break;
   }
}

int main()
{
   AnimalType type = AnimalType::bird;
   printLegCount(type);
   return 0;
}

实际上我的代码中的AnimalType枚举很大(40个或更多条目)。 printLegCount中的开关也很大。我有一些类似printLegCount的函数可以用switch case写入。

因此,每当我需要实例化某些类时,我最终会编写一个冗长而可怕的切换案例。我输入的所有内容都是运行时枚举值(在编译时无法解析)。一个枚举值对应一个类。起初我想写一个向量或数组,但我的类不共享任何共同的祖先。实际上我不想要一个共同的祖先,因为我不能有任何常用的方法,这就是printLegCount返回T的原因,以模仿我的生产代码。共同的祖先是一个空壳,我必须转发到真实类型才能使用它。

有没有办法写出类似的东西:

void printLegCount(AnimalType animalType)
{
   no_idea_how_to_write_it(animalType).printLegCount(); // h4lp plx
}

而不是巨大的开关案例。

2 个答案:

答案 0 :(得分:3)

你在谈论这样的事吗?

#include <iostream>

enum class AnimalType
{
   bird,
   mammal,
   lizard,
   fish,
};

template<AnimalType LEGCOUNT, typename T>
class AbstractAnimal
{
public:
   T printLegCount() { std::cout << (int)LEGCOUNT << std::endl; return 0; }
protected:
};

class Mammal : public AbstractAnimal<AnimalType::mammal, double>{};
class Bird : public AbstractAnimal<AnimalType::bird, int>{};
class Lizard : public AbstractAnimal<AnimalType::lizard, float>{};
class Fish : public AbstractAnimal<AnimalType::fish, long long>{};


template<AnimalType A, class T> void printLegCount() {
  AbstractAnimal<A, T>{}.printLegCount();
}

void foo() {
  printLegCount<AnimalType::bird, int>();
}

答案 1 :(得分:3)

您可以创建一个调度员:

// Same order as enum AnimalType
using AnimalTuple = std::tuple<Bird, Mammal, Lizard, Fish>;

// Helper function as gcc has problem to expand variadic for the lambda
template <typename T, typename F>
std::function<void()> call_with_default(F&& f)
{
    return [f]() {f(T{});};
}

template <typename F, std::size_t...Is>
void dispatch(AnimalType animalType, F&& f, std::index_sequence<Is...>)
{
    std::function<void()> fs[] = {
        call_with_default<std::tuple_element_t<Is, AnimalTuple>>(f)...
    };
    fs[static_cast<int>(animalType)]();
}

template <typename F>
void dispatch(AnimalType animalType, F f)
{
    constexpr auto AnimalCount = std::tuple_size<AnimalTuple>::value
    dispatch(animalType, f, std::make_index_sequence<AnimalCount>{});
}

然后叫它:

dispatch(animalType, [](auto t) { t.printLegCount(); });

Demo

如果您不能使用C ++ 14,则必须创建通用仿函数

struct LegCountPrinter {
    template <typename T>
    void operator(T t) const { t.printLegCount(); }
};

// later
dispatch(animalType, LegCountPrinter{});