在存在不可预测的类型别名的情况下如何处理显式模板实例化?

How to approach explicit template instantiation in the presence of unpredictable type aliases?

我正在尝试将一些 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 intsize_t,也许,uint32_t unsigned int,也许吧。因此,根据构建目标,"non-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>;

我的目标是能够列出我使用的类型,就像我使用它们一样,尽可能少地大惊小怪,并且不需要为不同的目标硬编码列表的变体。

我想在 C++14 中我可以用一个 if constexpr 和一些 std::is_same 来解决这个问题,至少如果在命名空间范围内允许的话。

如何在 C++11 中实现?

放弃 unsigned int 和 size_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>;

或者,您可能有待实例化类型的类型列表,其中只有重复类型被替换为虚拟类型(希望在 link 时间优化掉)。

在 C++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 预处理器之类的东西来明确地避免 'iterating' 显式实例化并编写一个直接接受类型列表的宏 ...

这是一种方法:

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

可能 improvements/alternatives:

  • 进行菊花链调用。然后在一个没有被任何人调用的导出函数中调用底部的那个。模板将被实例化以导出该函数,稍后在链接时将删除导出的函数

  • 求和-sizeof

我不确定 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{});   
}

(我并不是说这是最好的方法。)