如何简洁地编写大量显式函数模板实例化?

问题描述 投票:0回答:5

我正在编写一个 C++ 库,其中包含许多我想要显式实例化和导出多个类型参数的函数模板。在我的特定情况下,我有很多数字函数模板,我想分别实例化和编译

float
double
long double
。它们看起来像这样:

template <typename T>
T calculate_a(T x) { ... }

template <typename T>
T calculate_b(T x, T y) { ... }

// ...

如果我有 M 个函数模板和 N 个基础类型,那么我就有 M*N 个显式实例化要输入。这些实例化可以写得更简洁吗?

我当前的解决方案是使用预处理器宏来执行给定类型的所有实例化:

#define EXPLICITLY_INSTANTIATE(T) \
    template T calculate_a<T>(T x); \
    template T calculate_b<T>(T x, T y); \
    // ...

EXPLICITLY_INSTANTIATE(float);
EXPLICITLY_INSTANTIATE(double);
EXPLICITLY_INSTANTIATE(long double);

但是,这是次优的,因为它需要我单独维护每个函数模板签名的另一个副本。另外,如果我想在多个翻译单元中执行此操作,那么我需要单独维护每个翻译单元中的基础类型列表。 (假设 C++2a 添加了我想要支持的

long long double
类型;我必须向每个文件添加
EXPLICITLY_INSTANTIATE(long long double);
。)

另一种可能的方法是将我的所有函数收集到一个(仅静态)模板类中:

template <typename T>
class calculate {
    T a(T x) { ... }
    T b(T x, T y) { ... }
};

template class calculate<float>;
template class calculate<double>;
template class calculate<long double>;

这解决了第一个问题,即分别维护每个签名的两份副本,但需要我将每次调用

calculate_a
更改为
calculate::a<T>
。它没有解决第二个问题。

c++ templates instantiation explicit-instantiation
5个回答
7
投票

您可以通过获取模板的地址来实例化模板,从而避免重复函数签名:

// forward declarations in a header file
template<typename T>
T square(T num);

template<typename T>
T add(T left, T right);

// implementations and instantiations in a single implementation file
template<typename T>
T square(T num) {
    return num * num;
}

template<typename T>
T add(T left, T right) {
    return left + right;
}

// instantiations for specific types
#include <tuple>

template<typename... Ts>
auto instantiate() {
    static auto funcs = std::tuple_cat(std::make_tuple(
        add<Ts>,
        square<Ts>
    )...);

    return &funcs;
}

template auto instantiate<int, double>();

这里的开销是指向所有实例化函数的单个指针数组,例如 godbolt


4
投票

这就是 X Macros 的用途。它的工作原理非常简单。

您有一个文件,其中包含您想要应用此功能的所有类型。我们将其命名为“type_list.inc”。它看起来像这样:

X(float)
X(double)
X(long double)

当您想要对该类型列表执行某些操作时,您

#include
文件,但在包含点周围,您
#define
X
来执行您想要执行的操作:

#define X(T) \
    template T calculate_a<T>(T x); \
    template T calculate_b<T>(T x, T y); \
    // ...
#include "type_list.inc"
#undef X

你仍然需要维护两套函数原型。但你只需要维护一个类型列表。


1
投票

我没有清楚地表达我的问题意图。我显式实例化的目的不是限制可以调用这些函数的类型,而是通知编译器为 float、double 和 long double 生成可执行代码

好吧...如果您的所有类型都是默认可构造的(如

float
double
long double
)...在模板
foo()
函数中使用折叠,如下

template <typename ... Ts>
void foo ()
 { ((calculate_a(Ts{}), calculate_b(Ts{}, Ts{})), ...); }

并用所需类型调用

foo()

foo<float, double, long double>();

我想应该可以。

以下是完整的编译示例

template <typename T>
T calculate_a (T x)
 { return x; }

template <typename T>
T calculate_b (T x, T y)
 { return x+y; }

template <typename ... Ts>
void foo ()
 { ((calculate_a(Ts{}), calculate_b(Ts{}, Ts{})), ...); }

int main ()
 {
   foo<float, double, long double>();
 }

0
投票

对您想要的类型使用常规重载,并让它们调用函数模板,如下所示:

float calculate_a(float x) { return calculate_a<float>(x); }
float calculate_b(float x, float y) { return calculate_b<float>(x, y); }

double calculate_a(double x) { return calculate_a<double>(x); }
double calculate_b(double x, double y) { return calculate_b<double>(x, y); }

long double calculate_a(long double x) { return calculate_a<long double>(x); }
long double calculate_b(long double x, long double y) { return calculate_b<long double>(x, y); }

0
投票

我找到了一个很好的解决方案,当你需要实例化许多参数组合的模板时,如何在“循环”中完成:如何在“循环”中实例化模板方法?

© www.soinside.com 2019 - 2024. All rights reserved.