[英]Partially Specialized Structs vs Overloaded function template
眾所周知,函數模板不能部分專門用於C ++。 當您在概念上嘗試實現此目標時,您可以使用兩種可能的解決方案。 其中一個是使用帶有靜態函數的結構,可選地用模板函數包裝,如下所示:
template <class T, class U>
struct BarHelper
{
static void BarHelp(T t, const U& u)
{
std::cerr << "bar general\n";
}
};
template <class T>
struct BarHelper<T, double>
{
static void BarHelp(T t, const double& u)
{
std::cerr << "bar specialized\n";
}
};
template <class T, class U>
void bar(T t, const U& u)
{
BarHelper<T, U>::BarHelp(t, u);
};
bar
這里是可選的,你可以直接使用struct的靜態成員(盡管你必須明確指定所有參數)。
另一種方法是重載函數模板:
template <class T, class U>
void func(T t, const U& u)
{
std::cerr << "func general\n";
}
template <class T>
void func(T t, const double& u)
{
std::cerr << "func specialized\n";
}
對我來說,似乎第二種方法更可取。 對於初學者來說,它更加冗長,而且對於意圖更加清晰(我們正在編寫函數,所以讓我們使用函數而不是無意義的包裝器結構)。 此外,您可以使用一些很好的技巧來控制重載分辨率。 例如,您可以在繼承層次結構中包含非模板化“標記”參數,並使用隱式轉換來控制函數的優先級。 每當你在重載中具體指定一個類型時,你也會得到隱式轉換,如果你不喜歡這種行為,你可以在你的重載上使用enable_if來阻止它(讓你回到與結構相提並論)。
是否有理由偏愛部分專業結構? 這些原因有多普遍? 即哪個應該是你的“默認”? 如果您:a)計划自己實現所有特化,而b)這是否用作用戶可以注入自己行為的自定義點,這會有所不同嗎?
Herb Sutter有一篇關於避免功能模板專業化的着名博文。 在其中,他還建議(接近結尾)偏愛部分專門的結構來重載功能模板,但他似乎沒有給出任何具體的理由: http : //www.gotw.ca/publications/mill17.htm 。
道德#2:如果你正在編寫一個函數庫模板,寧願把它寫成一個永遠不應該專門化或過載的單個函數模板
(重點補充)。
讓我們先列出創建同一模板方法的幾個變體的選項:
簡單的重載:這可以起作用, 正如提問和演示的問題 。
但是,它並不總是很好,我們將在下面看到。
使用仿函數類部分特化:這是沒有模板函數特化的直接替代方法。
along with template functions overloading: this approach can be selected when the simple template overloading doesn't work, see below. 使用以及模板函數重載:當簡單模板重載不起作用時,可以選擇此方法,請參見下文。
編輯:添加@Nir選項4
---編輯結束---
該問題提出了一種情況,當模板參數從調用中推導出來時,模板函數重載工作正常。 但是,如果對模板函數的調用直接提供模板參數,並且需要根據模板參數的關系或條件匹配實現,則重載不再有用。
template <typename T, T val1, T val2>
void isSame1() {
cout << "val1: " << val1 << ", val2: " << val2 << " are "
<< (val1==val2?" ":"NOT ") << "the same" << endl;
}
雖然在編譯時val1和val2是已知的,但是我們無法在編譯時知道它們是相同的情況下部分特殊化。 在這種情況下,函數重載沒有幫助,在兩個非類型模板參數具有相同值的情況下沒有重載。
通過類部分特化,我們可以做到:
template <typename T, T val1, T val2>
struct IsSameHelper {
static void isSame() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
};
// partial specialization
template <typename T, T val>
struct IsSameHelper<T, val, val> {
static void isSame() {
cout << "val1: " << val << ", val2: " << val << " are the same" << endl;
}
};
template <typename T, T val1, T val2>
void isSame2() {
IsSameHelper<T, val1, val2>::isSame();
}
we can do: 或者,使用我們可以:
template<typename T, T val1, T val2>
struct is_same_value : std::false_type {};
template<typename T, T val>
struct is_same_value<T, val, val> : std::true_type {};
template <typename T, T val1, T val2>
typename std::enable_if<!is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are NOT the same" << endl;
}
template <typename T, T val1, T val2>
typename std::enable_if<is_same_value<T, val1, val2>::value, void>::type isSame3() {
cout << "val1: " << val1 << ", val2: " << val2 << " are the same" << endl;
}
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame1<int, 3, 4>();
isSame1<int, 3, 3>();
isSame1<int*, &global1, &global1>();
isSame1<int*, &global1, &global2>();
isSame2<int, 3, 4>();
isSame2<int, 3, 3>();
isSame2<int*, &global1, &global1>();
isSame2<int*, &global1, &global2>();
isSame3<int, 3, 4>();
isSame3<int, 3, 3>();
isSame3<int*, &global1, &global1>();
isSame3<int*, &global1, &global2>();
}
編輯:添加@Nir選項4
template <class T, T v> struct foo{
static constexpr T val = v;
};
// in a .cpp
template <class T, T v>
constexpr T foo<T, v>::val; // required for non-integral / non-enum types
template <class T, T v1, T v2> void isSame4(foo<T, v1> f1, foo<T, v2> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are NOT the same" << endl;
}
template <class T, T v> void isSame4(foo<T, v> f1, foo<T, v> f2) {
cout << "val1: " << f1.val << ", val2: " << f2.val << " are the same" << endl;
}
此選項的主要內容如下:
int global1 = 3;
int global2 = 3;
//======================================================
// M A I N
//======================================================
int main() {
isSame4(foo<int, 4>(), foo<int, 3>());
isSame4(foo<int, 3>(), foo<int, 3>());
isSame4(foo<int*, &global1>(), foo<int*, &global1>());
isSame4(foo<int*, &global1>(), foo<int*, &global2>());
}
我認為選項4的語法沒有任何優勢。 但是人們可以不這么認為......
注意在選項4中需要一個.cpp文件,對於T foo::val
的聲明,在所有其他選項中,一切都適用於.h文件。
---編輯結束---
基於模板元編程,我們可以獲得編譯時解析的情況,需要部分專業化。 這可以通過類部分特化或使用enable_if來實現(對於其條件,它又需要其自己的類部分特化)。
請參閱代碼: http : //coliru.stacked-crooked.com/a/65891b9a6d89e982
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.