I’m trying to write code that uses a member typedef of a template argument, but want to supply a default type if the template argument does not have that typedef. A simplified example I’ve tried is this:
struct DefaultType { DefaultType() { printf("Default "); } };
struct NonDefaultType { NonDefaultType() { printf("NonDefault "); } };
struct A {};
struct B { typedef NonDefaultType Type; };
template<typename T, typename Enable = void> struct Get_Type {
typedef DefaultType Type;
};
template<typename T> struct Get_Type< T, typename T::Type > {
typedef typename T::Type Type;
};
int main()
{
Get_Type<A>::Type test1;
Get_Type<B>::Type test2;
}
I would expect this to print “Default NonDefault”, but instead it prints “Default Default”. My expectation is that the second line in main() should match the specialized version of Get_Type, because B::Type exists. However, this does not happen.
Can anyone explain what’s going on here and how to fix it, or another way to accomplish the same goal?
Thank you.
Edit:
Georg gave an alternate method, but I’m still curious about why this doesn’t work. According the the boost enable_if docs, a way to specialize a template for different types is like so:
template <class T, class Enable = void>
class A { ... };
template <class T>
class A<T, typename enable_if<is_integral<T> >::type> { ... };
template <class T>
class A<T, typename enable_if<is_float<T> >::type> { ... };
This works because enable_if< true > has type as a typedef, but enable_if< false > does not.
I don’t understand how this is different than my version, where instead of using enable_if I’m just using T::Type directly. If T::Type exists wouldn’t that be the same as enable_if< true >::type in the above example and cause the specialization to be chosen? And if T::Type doesn’t exist, wouldn’t that be the same as enable_if< false >::type not existing and causing the default version to be chosen in the above example?
To answer your addition – your specialization argument passes the member typedef and expects it to yield
voidas type. There is nothing magic about this – it just uses a default argument. Let’s see how it works. If you sayGet_Type<Foo>::type, the compiler uses the default argument ofEnable, which isvoid, and the type name becomesGet_Type<Foo, void>::type. Now, the compiler checks whether any partial specialization matches.Your partial specialization’s argument list
<T, typename T::Type>is deduced from the original argument list<Foo, void>. This will deduceTtoFooand afterwards substitutes thatFoointo the second argument of the specialization, yielding a final result of<Foo, NonDefaultType>for your partial specialization. That doesn’t, however, match the original argument list<Foo, void>at all!You need a way to yield the
voidtype, as in the following:Now this will work like you expect. Using MPL, you can use
alwaysinstead oftovoid