I'm creating a small metaprogramming oriented module in one of my libraries, that uses a List<Ts...>
class for compile-type type list manipulation.
I specialize List
with an empty parameter pack to specialize certain metafunctions or typedefs. However, a lot of typedefs or metafunctions have the same implementation in both List<>
and List<Ts...>
.
Example:
template<typename...> struct List;
template<> struct List<>
{
using Type = List<>; // Redundant
using Tuple = std::tuple<>; // Redundant
// ...other redundant typedefs...
template<typename TList> using Append = TList; // Non-redundant
// ...other non-redundant typedefs...
};
template<typename... Ts> struct List
{
using Type = List<Ts...>; // Redundant
using Tuple = std::tuple<Ts...>; // Redundant
// ...other redundant typedefs...
template<typename TList>
using Append = AppendImpl<Type, TList>; // Non-redundant
// ...other non-redundant typedefs...
};
As you can see, some typedefs are redundant between List<>
and List<Ts...>
.
What I would like to do is similar to this:
template<typename...> struct List;
template<typename... Ts> struct ListBase
{
using Type = List<Ts...>;
using Tuple = std::tuple<Ts...>;
};
template<> struct List<> : public ListBase<>
{
template<typename TList> using Append = TList;
};
template<typename... Ts> struct List : public ListBase<Ts...>
{
template<typename TList>
using Append = AppendImpl<Type, TList>;
};
// The lines below should be valid code:
using X0 = List<>::Type;
using X1 = List<int, char, int>::Type;
using X2 = List<>::Tuple;
using X3 = List<char, char>::Tuple;
using X4 = List<>::Append<List<int, float>>;
using X5 = List<int>::Append<List<float>>;
Unfortunately, typedefs do not propagate from the base class to the derived one. As sbabbi said in the comments, you have to resort to using the full qualified name to refer to a typedef in the base class, which is not acceptable for the List<...>
class I'm designing.
Is there any way I can avoid this repetition without resorting to macros?