Как подойти к явному экземпляру шаблона при наличии непредсказуемых псевдонимов типов?
Я пытаюсь добавить в проект 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