I am trying to understand how to use type traits with std::enable_if to "enable" partial specializations of a class. Here is the example code I am attempting to get workingL
#include <type_traits>
#include <iostream>
class AbstractFoo {
public:
virtual const char* name() const = 0;
};
template <typename T, typename Enable = void>
class Foo;
template <>
class Foo<int> : public AbstractFoo{
public:
const char* name() const override { return "int"; }
};
template <>
class Foo<char> : public AbstractFoo {
public:
const char* name() const override { return "char"; }
};
template <typename T>
class Foo<T, typename std::enable_if<std::is_enum<T>::value, T>::type> : public AbstractFoo {
public:
const char* name() const override { return "enum"; }
};
enum class MyEnum {
VAL1,
VAL2,
VAL3
};
int main() {
Foo<int> v1;
Foo<char> v2;
Foo<MyEnum> v3;
std::cout << "v1.name()=" << v1.name() << std::endl;
std::cout << "v2.name()=" << v2.name() << std::endl;
std::cout << "v3.name()=" << v3.name() << std::endl;
return 0;
};
My goal is to have specific specializations for certain types (e.g., int and char), but, if the type is an enum, to use my partial specialization. However, when I attempt to compile this code, I get the following error
error: aggregate 'Foo v3' has incomplete type and cannot be defined
Which I assume means my specialization was not chosen and thus never becomes defined. What am I doing wrong?
CodePudding user response:
In the declaration of Foo
the second template parameter defaults to void
. That means that the following variable:
Foo<MyEnum> v3;
is actually
Foo<MyEnum, void> v3;
Now the question is: does this correspond to the specialization you want? Not really, because in your specialization for enum:
std::is_enum<T>::value = true
whenT = MyEnum
std::enable_if<std::is_enum<T>::value, T>::type = T = MyEnum
whenT=MyEnum
So, for T=MyEnum
, your give the specialization for Foo<T, T>
, which does not match the variable above.
As noted in a comment, a simple fix is to declare the specialization as
class Foo<T, typename std::enable_if<std::is_enum<T>::value>::type>
In this way, the second template parameter is void
when T=MyEnum
, so it matches the variable declaration of v3
.