Как подойти к явному экземпляру шаблона при наличии непредсказуемых псевдонимов типов?

Я пытаюсь добавить в проект extern template, чтобы ускорить время сборки и уменьшить размер диска на диске во время сборки.

Я сделал это, перечисляя специализации, которые я часто использую, например.

extern template class std::vector<std::string>;
extern template class std::vector<unsigned int>;
extern template class std::vector<uint32_t>;
extern template class std::vector<size_t>;

Проблема в том, что unsigned int есть size_t, может быть, и uint32_t есть unsigned int, может быть. Таким образом, в зависимости от цели сборки компиляция не подходит для разнообразия списка "не extern", которая фактически создает специализации:

template class std::vector<std::string>;
template class std::vector<unsigned int>;
template class std::vector<uint32_t>;
template class std::vector<size_t>;

Ошибка выглядит так (строка и номер столбца неточно):

templates-impl.h:15:36: error: duplicate explicit instantiation of ‘class std::vector<long unsigned int>’ [-fpermissive]
 template class std::vector<size_t>;

Моя цель состоит в том, чтобы просто указать типы, которые я использую, как я их использую, с минимумом суеты и без необходимости жесткого кодирования вариантов списка для разных целей.

Я предполагаю, что в С++ 14 я мог бы решить это с помощью if constexpr и некоторых std::is_same, по крайней мере, если бы это было разрешено в области пространства имен.

Как я могу сделать это в С++ 11?

Ответ 1

В качестве альтернативы вы можете иметь типы-типы типов, которые должны быть экземплярами, из которых только повторяющиеся типы заменяются фиктивными типами (мы надеемся, что они будут оптимизированы во время связи).

В С++ 11 что-то вроде:

template<typename... V>
struct explicit_instlist
{
    template<int I>
    struct get
    {
        using VI = typename std::tuple_element<I,std::tuple<V...>>::type;

        using type = typename std::conditional< is_first_at<I,VI,V...>::value,
            VI, dummy_inst<I,VI> >::type;
    };
};

template<unsigned I>
using my_list = typename explicit_instlist<
    std::string,
    unsigned int,
    uint32_t,
    size_t
    >::template get<I>::type;

/*extern*/ template class std::vector< my_list<0> >;
/*extern*/ template class std::vector< my_list<1> >;
/*extern*/ template class std::vector< my_list<2> >;
/*extern*/ template class std::vector< my_list<3> >;

где dummy_inst<I,T> генерирует уникальный фиктивный тип, который будет использоваться вместо T, когда он уже используется, и is_first_at работает как:

template<int I,typename T>
struct dummy_inst {};

template<int I,typename T,typename... V>
struct is_first_at {};

template<int I,typename T,typename V,typename... Vs>
struct is_first_at<I,T,V,Vs...>: std::conditional< std::is_same<T,V>::value,
    std::integral_constant<bool,I==0>,
    is_first_at<I-1,T,Vs...> >::type {};

ясно, dummy_inst должен быть специализированным, если шаблон с явно выраженным экземпляром не поддерживает пустое значение по умолчанию. Что-то вроде препроцессора boost может использоваться, чтобы избежать экспликации "итерации" явных настроек и записи макроса, принимающего список типов напрямую...

Ответ 2

Как опускать беззнаковые int и special_t специализации и просто сделать их все на основе размера?

extern template class std::vector<uint8_t>;
extern template class std::vector<uint16_t>;
extern template class std::vector<uint32_t>;
extern template class std::vector<uint64_t>;

Ответ 3

Я не уверен, как работает extern, но если речь идет о явном экземпляре шаблона, возможно, вы можете использовать вызов функции, подобный этому. Тип дублирования не должен генерировать ошибку.

template<class T>
void instance(T const&){std::vector<T> v{}; (void)v;}

void instantiate(){
   instance(std::string{}):
   instance(unsigned int{});
   instance(uint32_t{});
   instance(size_t{});   
}

(Я не говорю, что это лучший метод.)

Ответ 4

Вот как это сделать:

#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;

Возможные улучшения/альтернативы:

  • Сделайте цепочный вызов. Затем вызовите нижнюю в экспортируемой функции, которая не вызывается кем-либо. Шаблоны будут созданы для экспорта этой функции, а позже при связывании экспортированной функции будут удалены

  • Сделайте сумму sizeof