template argument deduction with strongly-typed enumerations

Useless picture Useless · Feb 22, 2012 · Viewed 18k times · Source

If I have a normal (weak) enumeration, I can use its enumerated values as non-type template parameters, like so:

enum { Cat, Dog, Horse };

template <int Val, typename T> bool magic(T &t)
{
    return magical_traits<Val>::invoke(t);
}

and call it as: magic<Cat>(t)

as far as I can see, if I have a strongly-typed enumeration and don't want to hard-code the enumeration type, I end up with:

enum class Animal { Cat, Dog, Horse };

template <typename EnumClass, EnumClass EnumVal, typename T> bool magic(T &t)
{
    return magical_traits<EnumVal>::invoke(t);
}

and now I have to write: magic<Animal, Animal::Cat>(t), which seems redundant.

Is there any way to avoid typing out both the enum class and the value, short of

#define MAGIC(E, T) (magic<decltype(E), E>(T));

Answer

Sopel picture Sopel · Nov 25, 2017

You can do it like this, if you can use C++17

#include <type_traits>

enum class Animal { Cat, Dog, Horse };

template <typename EnumClass, EnumClass EnumVal> 
void magic_impl()
{
    static_assert(std::is_same_v<EnumClass, Animal>);
    static_assert(EnumVal == Animal::Cat);
}

template <auto EnumVal>
void magic()
{
    magic_impl<decltype(EnumVal), EnumVal>();
}

int main()
{
    magic<Animal::Cat>();
}

demo: http://coliru.stacked-crooked.com/a/9ac5095e8434c9da