Ignoring duplicate explicit instantiations of template classes in C++

You could find another way to explicitly instantiate the template in a way that you can do metaprogramming on it.

Then instead of doing one instantiation per line, do them all in a pack. Run an n^2 algorithm on them (at compile time) to eliminate duplicates (or, honestly, you could probably skip that: depending on how you instantiate the template, it might not care).

Something like this, assuming Instantiate< Template, types< blah, foo, bar > > actually instantiates the list on the template passed in as the first argument:

#include <utility>
#include <type_traits>

template<typename T>
struct Test {};

template<typename... Ts>
struct types {};

template<template<typename>class Template, typename Types>
struct Instantiate {};

template<template<typename>class Template, typename T0, typename... Ts>
struct Instantiate<Template, types<T0, Ts...>>:
  Instantiate<Template, types<Ts...>>
{
  Template<T0>& unused();
};

template<typename U, typename Types>
struct prepend;

template<typename U, template<typename...>class pack, typename... Ts>
struct prepend< U, pack<Ts...> > {
  typedef pack<U, Ts...> types;
};
template<typename U, typename Types>
using Prepend = typename prepend<U, Types>::types;

template<typename U, typename Types, typename=void>
struct remove_type_from_types;
template<typename U, template<typename...>class pack>
struct remove_type_from_types<U, pack<>, void>
{
  typedef pack<> types;
};

template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
  typename std::enable_if< std::is_same<U, T0>::value >::type
>: remove_type_from_types< U, pack<Ts...> >
{};

template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
  typename std::enable_if< !std::is_same<U, T0>::value >::type
>
{
  typedef Prepend< T0, typename remove_type_from_types< U, pack<Ts...> >::types > types;
};

template<typename Types>
struct remove_duplicates {
  typedef Types types;
};

template<template<typename...>class pack, typename T0, typename... Ts>
struct remove_duplicates<pack<T0, Ts...>> {
private:
  typedef typename remove_type_from_types< T0, pack<Ts...> >::types filtered_tail;
  typedef typename remove_duplicates< filtered_tail >::types unique_tail;
public:
  typedef Prepend< T0, unique_tail > types;
};
template<typename Types>
using RemoveDuplicates = typename remove_duplicates<Types>::types;

static Instantiate<Test, RemoveDuplicates<types<int, double>> > unused;

int main() {

}

As noted, you can probably do away with the entire eliminate-duplicates bit, because of how I'm instantiating the use of the template. I am also not sure if the above use of each template is sufficient to instantiate it (ie, that it won't be optimized away somehow, and that the symbol will be exported).

(Recursion depth is n in the number of types, and total work done is n^2 in the number of types: that is shallow enough and fast enough for any reasonable number of types, I suspect. Fancier unique type removal is difficult, due to lack of weak ordering on naked types...)


Don't specialize for the typedefs, instead specialize for the relevant underlying types (such as int). That way you can typedef as many/few times as you like and you still always get the specializations you want.


You can define a preprocessor flag for you configuration and then put template inside an #ifdef block.