c z
c z

Reputation: 8967

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

Upvotes: 3

Views: 1742

Answers (2)

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145239

You cannot partially specialize a function template, but can you just let it forward to a class template.

Since your function doesn't have arguments that's particularly easy:

#include <iostream>
#include <type_traits>

namespace impl {
    using namespace std;

    template< class Type, bool is_enum_ = is_enum<Type>::value >
    struct Foo;

    template< class Type >
    struct Foo<Type, true>
    { void func() { cout << "Enum" << endl; } };

    template<>
    struct Foo<int>
    { void func() { cout << "Int" << endl; } };

    template<>
    struct Foo<float>
    { void func() { cout << "Float" << endl; } };

}  // namespace impl

template< class Type >
void foo()
{ impl::Foo<Type>().func(); }

auto main()
    -> int
{
    enum class An_enum
    {
        alpha, beta,
    };

    foo<An_enum>();
    foo<int>();
    foo<float>();
    #ifdef TEST
        foo<char>();    //! Doesn't compile.
    #endif
}

With arguments you can use “perfect forwarding” (which isn't all that perfect, really, but usually good enough) via std::forward.

Upvotes: 2

skypjack
skypjack

Reputation: 50540

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.

Upvotes: 3

Related Questions