我怎样才能简明扼要地编写大量显式函数模板实例?

Dav*_*ang 6 c++ templates instantiation explicit-instantiation

我正在编写一个C++库,其中包含许多我希望显式实例化并导出多个类型参数的函数模板.在我的具体情况,我有很多的,我想单独实例化和编译数值函数模板float,doublelong double.他们看起来像这样:

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

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

// ...
Run Code Online (Sandbox Code Playgroud)

如果我有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);
Run Code Online (Sandbox Code Playgroud)

但是,这不是最理想的,因为它要求我单独维护每个函数模板签名的另一个副本.另外,如果我想在多个翻译单元中执行此操作,那么我需要单独维护每个翻译单元中的基础类型列表.(假设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>;
Run Code Online (Sandbox Code Playgroud)

这解决了单独维护每个签名的两个副本的第一个问题,但要求我改变的每次调用calculate_acalculate::a<T>.它没有解决第二个问题.

小智 8

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

// 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>();
Run Code Online (Sandbox Code Playgroud)

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


Nev*_*vin 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); }
Run Code Online (Sandbox Code Playgroud)