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 simpli
First step: stop using "Type" and use the mpl standard "type".
BOOST_MPL_HAS_XXX_DEF(Type)
template < typename T >
struct get_type { typedef typename T::Type type; };
template < typename T >
struct calculate_type : boost::mpl::if_
<
has_Type<T>
, get_type<T>
, boost::mpl::identity<default_type>
>::type {}
typedef calculate_type<A>::type whatever;
If you used "type" instead of "Type" in your metafunctions you wouldn't require the fetcher "get_type" to convert it and could just return T in that case.
To answer your addition - your specialization argument passes the member typedef and expects it to yield void
as type. There is nothing magic about this - it just uses a default argument. Let's see how it works. If you say Get_Type<Foo>::type
, the compiler uses the default argument of Enable
, which is void
, and the type name becomes Get_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 deduce T
to Foo
and afterwards substitutes that Foo
into 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 void
type, as in the following:
template<typename T>
struct tovoid { typedef void type; };
template<typename T, typename Enable = void> struct Get_Type {
typedef DefaultType Type;
};
template<typename T>
struct Get_Type< T, typename tovoid<typename T::Type>::type > {
typedef typename T::Type Type;
};
Now this will work like you expect. Using MPL, you can use always
instead of tovoid
typename apply< always<void>, typename T::type >::type
You can do that by utilizing SFINAE:
template<class T> struct has_type {
template<class U> static char (&test(typename U::Type const*))[1];
template<class U> static char (&test(...))[2];
static const bool value = (sizeof(test<T>(0)) == 1);
};
template<class T, bool has = has_type<T>::value> struct Get_Type {
typedef DefaultType Type;
};
template<class T> struct Get_Type<T, true> {
typedef typename T::Type Type;
};