C++ 中一系列模板参数的显式模板实例化

zbh*_*047 5 c++ templates

创建库时,显式模板实例化非常有用。假设我有一个带有 int 参数的模板:

template <int i> struct S { ... };
Run Code Online (Sandbox Code Playgroud)

要执行显式模板实例化,语法类似于

template struct S<1>;
Run Code Online (Sandbox Code Playgroud)

但是,我只能以这种方式使用一行实例化一个实例。我想做的是以一种优雅的方式定义一系列模板。例如,考虑无法编译的错误代码:

#define MIN_I 1
#define MAX_I 16
for (int i = MIN_I; i <= MAX_I; i++) // i should be a constant
    template struct S<i>;
Run Code Online (Sandbox Code Playgroud)

这样,当MAX_I改变时,修改就非常简单了。我能实现这个目标吗?如果可能的话,有没有一种简单的方法可以做到这一点?谢谢你!

此外,这个问题可以推广到更一般的环境。例如,我可以采用 1,2,4,8,16,32,64,128,256 或一些预定义的序列。

我创建模板库的原因不好说。简而言之,我将创建一个在GPU上运行的CUDA库(由nvcc编译器编译),并由gcc编译的标准c++程序调用。

JaM*_*MiT 3

让我们从稍微转变一下视角开始。目标是实例化给定模板的某些实例。请注意,我删除了“显式”\xe2\x80\x93 这个词,虽然会有一个显式实例化,但它不必是有问题的模板。相反,我会显式实例化一个帮助器模板,该模板将隐式实例化所需的模板。

\n

帮助器模板将使用参数包来容纳任意数量的参数。如果目标只是列出数字,那么这将是相当简单的。然而,还希望能够指定最小值和最大值。为了处理这种情况,我将支持std::integer_sequence作为模板参数,我将通过部分专业化来处理它。

\n
// Start with a template that will not be defined; we will define only a\n// specialization of this.\n// The `C` parameter allows this to be applied to more templates than just `S`.\n// The other parameters will make more sense for the specialization.\ntemplate<template<int> typename C, int ADD, class T> struct force_instantiation_impl;\n\n// Partially specializing the template allows access to the numbers comprising the\n// `integer_sequence` parameter.\n// The ADD parameter will be added to each number in the sequence (will be helpful later).\ntemplate<template<int> typename C, int ADD, int... Ints>\nstruct force_instantiation_impl<C, ADD, std::integer_sequence<int, Ints...>> {\n    // Implicitly instantiate the desired templates.\n    std::tuple<C<ADD + Ints>...> unused;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

这里,参数包扩展用于迭代所有所需的模板参数。这起到了问题“错误代码”中循环的作用。当然,我们仍然需要获得给定最小值和最大值的索引。

\n

为了方便起见,如果您愿意的话,我将提供另一层帮助程序模板,即“界面”层。第一个接口助手允许仅指定模板、最小值和最大值。实例化此助手将导致所需的模板被实例化。

\n
// Instantiates C<I> for I ranging from MIN to MAX.\n// MAX must be at least as large as MIN.\ntemplate<template<int> typename C, int MIN, int MAX>\nstruct force_instantiation_range {\n    // Check the assumption.\n    static_assert(MIN <= MAX);\n    // Make an integer sequence from 0 to (MAX-MIN) for the template argument.\n    // When MIN is added to each element, the sequence will go from MIN to MAX.\n    force_instantiation_impl<C, MIN, std::make_integer_sequence<int, MAX-MIN+1>> unused;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

另一个接口帮助器允许简单地列出所需的参数。在某种程度上,这个助手的目的是隐藏因支持一系列参数而带来的复杂性。(如果没有这种支持,该模板的参数可能就是 的参数force_instantiation_impl。)

\n
// Instantiates C<I> for the given I\'s.\ntemplate<template<int> typename C, int... Ints>\nstruct force_instantiation_list {\n    force_instantiation_impl<C, 0, std::integer_sequence<int, Ints...>> unused;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

就是这样。由于注释的原因,它可能看起来有很多代码,但实际上相当短。存在三个结构模板定义,每个定义都有一个成员。要使用它,请显式实例化接口助手之一。

\n
// Forgive me, I\'m changing the pre-processor directives to type-safe constants.\nconstexpr int MIN_I = 1;\nconstexpr int MAX_I = 16;\ntemplate struct force_instantiation_range<S, MIN_I, MAX_I>;\n
Run Code Online (Sandbox Code Playgroud)\n

同样,可以明确列出参数。

\n
template struct force_instantiation_list<S, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16>;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

诚然,我确实稍微转移了问题。如果您确实需要直接对模板进行显式实例化,那么这种方法将不起作用。如果是这种情况,您可能必须依赖预处理器,这可能是一场噩梦。幸运的是,Boost.Preprocessor已经处理了最痛苦的细节。我会利用这一点。

\n