Is it possible to define an implementation template specialization as typedef of another type?

Viewed 6949

I have a class template for which I want to introduce several template specializations. Those template specializations identical to some existing type. Conceptually I would like to implement them as aliases/typedefs.

The following example code should show what I want to do:

template<class T> class Common {
    /// general implementation
};

class TypeZ;    

template<> class Common<Type1> = TypeZ; // <<< how to do this?
template<> class Common<Type2> = TypeZ;
template<> class Common<Type3> = TypeZ;

Is the above possible in some way in C++ (or C++11)? It would be great if I didn't have to implement Common<...> as a class that inherits TypeZ - the actual code is more complex than shown above and inheriting TypeZ is not a good idea there.

3 Answers

Assuming only certain specialisations of Common are aliases of TypeZ then you can do:

template<class T> class Common {
    struct type {
        /// general implementation
    };
};

template<> class Common<Type1> { using type = TypeZ; };
template<> class Common<Type2> { using type = TypeZ; };
template<> class Common<Type3> { using type = TypeZ; };

template<class T> using common_t = typename Common<T>::type;

Then you use common_t<T> rather than Common<T>.

Just to entertain the inheritance idea, have you tried this?

template<> class Common<Type1> : public TypeZ { using TypeZ::TypeZ; };
template<> class Common<Type2> : public TypeZ { using TypeZ::TypeZ; };
template<> class Common<Type3> : public TypeZ { using TypeZ::TypeZ; };

Then you don't need to use a nested type alias.

Yes you can, by using type aliases:

template<typename T> using Common = TypeZ;

See the link for more examples of what is possible. using can be used anywhere typedef can be used, plus in template aliases like the above, so I recommend using using over typedef everywhere you write C++11.


If you need a more complicated mapping, you can use some simple template metaprogramming using std::enable_if or std::conditional combined with std::is_same. For example, if you need to specialize for 3 types only, use this:

#include <type_traits>

class Type1 {};
class Type2 {};
class Type3 {};
class Type4 {};

class TypeZ {};

// Implementation 1
template<typename T>
constexpr bool is_Type123_func()
{
  return std::is_same<T, Type1>() || std::is_same<T, Type2>() || std::is_same<T, Type3>();
}
template<typename T>
using Common_byfunc = typename std::conditional<is_Type123_func<T>(), TypeZ, T>::type;

static_assert(std::is_same<Common_byfunc<Type1>, TypeZ>(), "");
static_assert(std::is_same<Common_byfunc<Type2>, TypeZ>(), "");
static_assert(std::is_same<Common_byfunc<Type3>, TypeZ>(), "");
static_assert(!std::is_same<Common_byfunc<Type4>, TypeZ>(), "");

// Implementation 2
template<typename T>
struct is_Type123 : public std::conditional<std::is_same<T, Type1>() || std::is_same<T, Type2>() || std::is_same<T, Type3>(), std::true_type, std::false_type>::type {};
template<typename T>
using Common = typename std::conditional<is_Type123<T>::value, TypeZ, T>::type;

static_assert(std::is_same<Common<Type1>, TypeZ>(), "");
static_assert(std::is_same<Common<Type2>, TypeZ>(), "");
static_assert(std::is_same<Common<Type3>, TypeZ>(), "");
static_assert(!std::is_same<Common<Type4>, TypeZ>(), "");

Both implementations are equivalent up to a name and the fact you have to use a function call operator () or the member accessor ::value in the std::conditional.

I am not sure that is allow or not in C++ standard about code on this question.
In my opinion, 'using' will create a new datatype, see the sample code below.

using TypeZ::TypeZ; to inherit the constructor.

#include <iostream>

template<typename T>
class Base { using type_universal = T; };

//e.g. Normal template specialization
template<> class Base<int> { using type_int = int; };

//Target:
class TypeZ {public: int I_am_typeZ = 1; };

//Alias:
template<> class Base<double> : public TypeZ { using TypeZ::TypeZ; };

int main() {
    Base<double> instance;
    std::cout<<"Base<double>.I_am_typeZ = "<<instance.I_am_typeZ<<std::endl;

    return 0;
}
Related