[英]C++ function template specialization declarations and template arguments; none vs. <> vs. <type>
在研究函數模板時,我看到以不同方式聲明的特化:
template<> void f(argtype) {}
template<> void f<>(argtype) {}
template<> void f<argtype>(argtype) {}
......我想知道這些之間的區別。 給出以下帶有和不帶參數的模板函數的例子,我有幾個問題。
#include <iostream>
#include <typeinfo>
//Function print1 WITH function parameter---------------------------------------------
template<class T>
void print1(T) { std::cout << "Primary template for print1() with type " << typeid(T).name() << std::endl; }
template<>
void print1<int>(int) { std::cout << "Specialization for print1<int>(int)" << std::endl; }
//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1<>(int) { std::cout << "Specialization for print1<>(int)" << std::endl; }*/
//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1(int) { std::cout << "Specialization for print1(int)" << std::endl; }*/
//Function print2 WITHOUT function parameter------------------------------------------
/*Not allowed together with print<>(); compiler complains:
t2.cpp:29:6: error: template-id 'print2<>' for 'void print2()' does not match any template declaration*/
/*template<class T>
void print2() { std::cout << "Primary template for print2()" << std::endl; }*/
template<class T = short> //Declaration of print2<>() now ok in conjunction with print2<>()
void print2() { std::cout << "Primary template for print2()" << std::endl; }
template<>
void print2<int>() { std::cout << "Specialization for print2<int>()" << std::endl; }
template<>
void print2<>() { std::cout << "Specialization for print2<>()" << std::endl; }
int main() {
//These three work in the same way, no matter which call method we use, so far so good
print1(10);
print1<>(10);
print1<int>(10);
print1(true);
print1<>(true);
print1<bool>(true);
print2(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
print2<>(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
print2<bool>(); //Triggers print2<bool>() primary template
print2<short>(); //Triggers print2<>(), should definately trigger primary template for print2()
print2<int>(); //Triggers print2<int>() specialization
return 0;
}
輸出:
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Primary template for print1() with type b
Primary template for print1() with type b
Primary template for print1() with type b
Specialization for print2<>()
Specialization for print2<>()
Primary template for print2()
Specialization for print2<>()
Specialization for print2<int>()
將模板特化參數保留為空,不存在或具有特殊類型以及它如何影響結果會產生什么特殊含義?
如果您確實提供了完整的模板參數列表,那么您只需要為給定的模板參數集明確地專門化函數模板。
如果為模板參數的(可能為空)子集提供參數,那么您將明確地為一組必須推導出的參數專門化函數模板。 根據[temp.deduct.decl]:
在聲明者id引用函數模板的特化的聲明中,執行模板參數推導以識別聲明引用的特化。 具體來說,這是為了顯式實例化(14.7.2),顯式特化(14.7.3)和某些朋友聲明(14.5.4)。 [...]。 在所有這些情況下,
P
是被認為是潛在匹配的函數模板的類型,A
是來自聲明[...]的函數類型。
扣除按照14.8.2.5中的描述完成。如果對於所考慮的功能模板集合,在考慮部分排序后(14.5.6.2),沒有匹配或多於一個匹配,則扣除失敗,並且在聲明情況下,程序是不正確的。
因此,對於沒有給出參數的每個參數,或者在根本沒有指定列表的情況下,對於特化的每個相應參數及其與主模板的對應參數進行模板參數推導。 該過程在§14.8.2.5中描述,就像我們使用提供的模板參數列表作為模板參數和專業化中參數類型的對象作為函數參數調用主模板一樣。
您應該熟悉一個事實,即可以使用指定的某些模板參數調用函數模板,例如
template <typename A, typename B> void foo(A, B);
foo(7684, 48.);
foo<int>(7684, 48.);
foo<int, double>(7684, 48.);
這相當於顯式特化:
template <typename T, typename U>
void foo(T, U) {}
// Both template arguments have to be deduced.
template<> void foo(double, float);
// The *exact* same as above.
// template<> void foo<>(double, float);
// Second template argument has to be deduced by type.
// If we call foo<int>(int(), float()) then the deduced specialization is
// foo<int, float>, thus U=float.
template<> void foo<int>(int, float);
template<> void foo<int, int>(int, int);
這也可以應用於函數模板的重載。 為了找到專業化對應的主要模板,選擇最專業的模板。
template <typename T, typename U>
void foo(T&, U&) {}
template <typename T, typename U>
void foo(T const&, U&) {}
// Specializes the second overload because it is more specialized.
template <>
void foo(int const&, float&);
請注意,在查找主模板時,使用提供的參數(即不推斷)來檢查主模板的結果函數參數與特化的結果函數參數。 他們必須是平等的。
template <typename T, typename U>
void foo(T&, U&) {}
// Error - no matching primary template found.
template <>
void foo<int, int>(float&, int&);
// Dito:
template <>
void foo<int>(int, int&);
似乎使用函數參數,此規范是多余的,無論指定方式如何,編譯器都會推導出它(結果是等效顯式規范成為不允許的重新聲明)。
是的,情況確實如此。 請考慮如果您無效地指定模板參數會導致錯誤:
但在這種情況下,意義似乎意味着更多的東西,而“空”的專業化(<>)是以某種無法預料的方式觸發的。 怎么會?
對於調用,首先推導出模板參數。 然后調用那些模板參數的特化。
如果你明確地為這個特殊的專業化設置了一個函數模板,這就是print2<>
,它是print2<short>
,那么就會調用那個顯式特化。
以何種方式無法預料?
為什么在使用
print2<>()
專門化print2
時必須有一個默認的模板參數,但不是沒有它?
因為編譯器無法推斷出參數。 如果你提供一個默認的說法,他沒有推斷它擺在首位。
將模板專門化參數留空是什么特殊含義
如果可能,推斷出缺失的論點; 空參數列表表示要推導出所有參數。
不存在的
這意味着您要聲明主要模板,而不是明確的專業化。
或者使用專門的類型
這意味着您要聲明該類型的顯式特化。
“空”特化(<>)以某種無法預料的方式觸發。 怎么會?
在這兩種情況下,都推導出模板參數。 在print1
,主模板聲明T
與函數參數的類型相同; 所以它是從函數參數中推導出來的。 在print2
,它使用默認類型short
,因此使用它。 因此,當您認為print2<>
應該是print2<short>
print2<>
時,您會驚訝地發現: print2<>
是 print2<short>
。
為什么在使用print2 <>()專門化print2時必須有一個默認的模板參數,但不是沒有它?
如果既沒有默認參數也沒有從中推導出參數的函數參數,那么就不可能推導出專門化的類型,因此無法使用<>
。 我不知道你的意思是“沒有它”; 如果您的意思是“沒有<>
”,那么您將聲明主模板,而不是專業化,並且參數是通用的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.