c z c z - 3 months ago 14
C++ Question

C++ specialise function on enum

Is it possible to specialise a template function on an

enum
?

I've seen noted here a template function can be disabled if it isn't an enum, but is this possible whilst still allowing other types?

My example below shows specialisations for
int
,
float
, and
enum
(it doesn't compile because it tries to overload the
enum
version rather than specialising it). I feel I'm missing something obvious.

Note that I'm looking to specialise on any enum, not just a named one (
EAnEnum
in the example)

#include <iostream>

enum class EAnEnum
{
Alpha,
Beta,
};

template<typename T>
void MyFunc();

template<>
void MyFunc<int>()
{
std::cout << "Int" << std::endl;
}

template<>
void MyFunc<float>()
{
std::cout << "Float" << std::endl;
}

// MyFunc<Enum>
template<typename T>
typename std::enable_if<std::is_enum<T>::value, void>::type MyFunc()
{
std::cout << "Enum" << std::endl;
}

int main()
{
MyFunc<EAnEnum>();
return 0;
}

Answer

You cannot partially specialize a function, but you can use tag dispatching instead.
It follows a minimal, working example based on the OP's question:

#include <iostream>
#include<type_traits>

enum class EAnEnum
{
    Alpha,
    Beta,
};

template<typename>
struct tag {};

void MyFunc(tag<int>)
{
    std::cout << "Int" << std::endl;
}

void MyFunc(tag<float>)
{
    std::cout << "Float" << std::endl;
}

void MyFunc(tag<EAnEnum>)
{
    std::cout << "Enum" << std::endl;
}

template<typename T>
void MyFunc() {
    MyFunc(tag<std::decay_t<T>>{});
}

int main()
{
    MyFunc<EAnEnum>();
    return 0;
}

You can easily add a parameter pack to be forwarded to the right MyFunc and still use this technique to solve your problem.
Of course, you can now specialize for any enum.
You can also provide a fallback MyFunc as:

template<typename T>
void MyFunc(tag<T>)
{
    std::cout << "Fallback" << std::endl;
}

If you want a fallback for all the possible enum types, you can now rely on SFINAE, for these are different overloaded functions:

template<typename T>
std::enable_if_t<std::is_enum<T>::value>
MyFunc(tag<T>)
{
    std::cout << "Fallback for enums only" << std::endl;
}

Note that you should not use directly the implications of MyFunc that accept a tag specialization as an entry point.
Those are meant as internal functions.
Use instead the generic one, as shown in the example.