Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 881
p12 Avatar asked Dec 06 '13 09:12

p12


2 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.

like image 53
Simple Avatar answered Oct 25 '22 13:10

Simple


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.

like image 29
rubenvb Avatar answered Oct 25 '22 14:10

rubenvb