17

Is it possible to check that a type is an instantiation of a particular template?

I have a class template where one of the template parameter must be either an instantiation of a particular template, or some other type. For instance, consider this simple definition of a typelist:

struct null_type;

template <typename Head, typename Tail>
struct typelist
{
    // Tail must be a typelist or null_type

    typedef Head head;
    typedef Tail tail;
};

Now, I would like to make sure that the type provided for the Tail template parameter is always either an instantiation of typelist or null_type. I could use partial specialization to define the template only for those cases, like this:

template <typename Head, typename Tail>
struct typelist; // default, not defined

template <typename Head, typename H, typename T>
struct typelist< Head, typelist<H,T> > // Tail = typelist, ok
{
    typedef Head head;
    typedef typelist<H,T> tail;
};

template <typename Head>
struct typelist< Head, null_type > // Tail = null_type, ok
{
    typedef Head head;
    typedef null_type tail;
};

However, I end up duplicating code, which is something I would like to avoid. Ideally, I'd need a trait to test whether a type is an instantiation of a template, to use it with enable_if or in static assertions:

#include <boost/mpl/or.hpp>
#include <type_traits>

struct null_type;

template <typename Head, typename Tail>
struct typelist
{
    static_assert(
        boost::mpl::or_<
            is_instantiation_of< typelist, Tail >,
            std::is_same< Tail, null_type >
        >::value,
        "Tail must be a typelist or null_type" );

    typedef Head head;
    typedef Tail tail;
};

Is such a trait (is_instantiation_of) already available in the standard library or in Boost? Is is possible to write one?

Luc Touraille
  • 79,925
  • 15
  • 92
  • 137

1 Answers1

24

I came up with the following solution, using C++11 variadic templates and simple partial specialization:

#include <type_traits>

template < template <typename...> class Template, typename T >
struct is_instantiation_of : std::false_type {};

template < template <typename...> class Template, typename... Args >
struct is_instantiation_of< Template, Template<Args...> > : std::true_type {};

It could be adapted to C++03 by using the preprocessor to generate versions for a varying number of template parameters, but there is maybe a simpler way.

Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • Tch, I was just about to hit "Post Your Answer". :P – Xeo Jun 28 '12 at 19:18
  • 5
    Btw, I call [my version](https://bitbucket.org/martinhofernandes/wheels/src/default/include/wheels/type_traits.h%2B%2B#cl-161) `is_specialization_of` because that's the correct term for this. "Instantiation" is the *process* of generating "specializations". – R. Martinho Fernandes Jun 28 '12 at 19:21
  • @R.MartinhoFernandes: I'd probably swap the template parameters, though, to be consistent with `is_base_of`. – Xeo Jun 28 '12 at 19:27
  • 1
    @Xeo: That is the inconsistent one! :P – R. Martinho Fernandes Jun 28 '12 at 19:28
  • @R.MartinhoFernandes: Nevermind, the "to be tested" subject is the first argument for both, so I guess it *is* consistent. – Xeo Jun 28 '12 at 19:32
  • Sorry guys, I spent some time thinking about this beforehand and thought it could be useful to others. Sorry if I made you loose your time! And, @R.MartinhoFernandes, thanks for the exact term! – Luc Touraille Jun 28 '12 at 19:33
  • Since recently, you can post a question and answer at the same time. Just tick the "answer yourself" box (or however it's called" below the "post question" button. – Xeo Jun 28 '12 at 19:41
  • @Xeo: Oh, thanks, I did not know that. This site keeps improving every day! – Luc Touraille Jun 28 '12 at 19:47
  • 6
    Beware, the solution barfs if the `Template` argument is a template that has non-type parameters, e.g. `template struct foo{...}` (gcc 4.7) – Mike Kinghan Aug 27 '12 at 09:08
  • 1
    Not working for `std::array` and all such kind of templates - see previous comment – PiotrNycz Mar 17 '15 at 11:33