#include <iostream>
#include <functional>
#include <tuple>

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

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

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

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

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)
{
    dispatch(animalType, f, std::make_index_sequence<std::tuple_size<AnimalTuple>::value>{});
}

void printLegCount(AnimalType animalType)
{
    auto legCountPrinter = [](auto t) { t.printLegCount(); };
    dispatch(animalType, legCountPrinter);
}

int main()
{
    printLegCount(AnimalType::mammal);
    printLegCount(AnimalType::fish);
}