Reputation: 8967
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
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
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