簡體   English   中英

我怎樣才能簡明扼要地編寫大量顯式函數模板實例?

[英]How can I concisely write a lot of explicit function template instantiations?

我正在編寫一個C ++庫,其中包含許多我希望顯式實例化並導出多個類型參數的函數模板。 在我的特定情況下,我有很多數字函數模板,我想單獨實例化和編譯floatdoublelong 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> 它沒有解決第二個問題。

這就是X Macros的用途。 它非常簡單。

您有一個文件,其中包含您要將其應用於的所有類型。 我們稱之為“type_list.inc”。 它看起來像這樣:

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

如果要對該類型列表執行某些操作,請#include該文件,但在包含點附近,您#defineX來執行您要執行的操作:

#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

您仍然需要維護兩組函數原型。 但是您只需要維護一個類型列表。

我沒有清楚地表達我對這個問題的意圖。 我的顯式實例化的目的不是限制可以調用這些函數的類型,而是通知編譯器為float,double和long double生成可執行代碼

好吧......如果所有類型都是默認的可構造的(如floatdoublelong double )...在模板中使用折疊foo()函數如下

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

並使用desidered類型調用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>();
 }

您可以通過獲取其地址來實例化模板,從而避免重復功能簽名:

// 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

對所需類型使用常規重載並讓它們調用函數模板,如下所示:

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); }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM