我正在编写一个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>
。它没有解决第二个问题。
答案 0 :(得分:2)
这就是X Macros的目的。它非常简单。
您有一个文件,其中包含您要将其应用于的所有类型。我们称之为“type_list.inc”。它看起来像这样:
#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}}
您仍然需要维护两组函数原型。但是你只需要维护一个类型列表。
答案 1 :(得分: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>();
}
答案 2 :(得分:1)
您可以通过获取地址实例化模板来避免重复功能签名:
// 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上的。
答案 3 :(得分: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); }