簡體   English   中英

函數模板重載

[英]function template overloading

有人可以總結一下函數模板重載的想法嗎? 什么重要,模板參數還是函數參數? 返回值呢?

例如,給定一個函數模板

template<typename X, typename Y> void func(X x, Y y) {}

什么是重載函數模板?

1) template<typename X> void func(X x, int y) {}
2) template<typename X, typename Y> X func(X x, Y y) {}
3) template<class X, class Y, class Z> void func(X x, Y y, Z z) {}

在該列表中,只有第二個引入了歧義,因為函數——無論它們是否是模板——都不能根據返回類型進行重載。

您可以使用其他兩個:

template<typename X> void func(X x, int y);

如果調用的第二個參數是 int,則將使用,例如func("string", 10);

template<class X, class Y, class Z> void func(X x, Y y, Z z);

如果您使用三個參數調用 func 將使用。


我不明白為什么其他一些答案提到模板函數和函數重載不混合。 他們當然會這樣做,並且有特殊的規則來選擇要調用的函數。

14.5.5

一個函數模板可以被其他函數模板和普通(非模板)函數重載。 普通函數與函數模板無關(即,它永遠不會被視為特化),即使它與潛在生成的函數模板特化具有相同的名稱和類型。)

非模板化(或“較少模板化”)重載優於模板,例如

template <class T> void foo(T);
void foo(int);

foo(10); //calls void foo(int)
foo(10u); //calls void foo(T) with T = unsigned

您使用一個非模板參數的第一個重載也屬於此規則。

如果在多個模板之間進行選擇,則首選更專業的匹配:

template <class T> void foo(T);
template <class T> void foo(T*);

int i;
int* p;
int arr[10];

foo(i);  //calls first
foo(p);   //calls second
foo(arr); //calls second: array decays to pointer

您可以在標准的同一章(函數模板)中找到所有規則的更正式的描述


最后,在某些情況下,兩個或多個重載會產生歧義:

template <class T> void foo(T, int);
template <class T> void foo(int, T);

foo(1, 2);

這里的調用是模棱兩可的,因為兩個候選人都同樣專業。

您可以使用(例如) boost::disable_if消除這種情況的歧義。 例如,我們可以指定當 T = int 時,第二個重載不應作為重載候選者包括在內:

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
template <class T>
void foo(T x, int i);

template <class T>
typename boost::disable_if<boost::is_same<int, T> >::type
foo(int i, T x);

foo(1, 2); //calls the first

這里庫在第二個重載的返回類型中產生“替換失敗”,如果 T = int,則將其從重載候選集中刪除。

在實踐中,您應該很少遇到這樣的情況。

這里有兩個獨立的東西:函數模板和函數重載。 任何兩個不同的模板聲明都可能是彼此的重載,因此您的問題並不如所述。 (你給出的三個“重載”不是建立在第一個模板之上,而是你有四個重載到同一個函數名。)真正的問題是,給定一些重載和一個 call ,如何調用所需的重載?

首先,無論是否涉及模板,返回類型都不參與重載過程。 所以#2 永遠不會和#1 搭配得很好。

其次,函數模板重載解析規則不同於更常用的類模板特化規則。 兩者本質上都解決了相同的問題,但是

  • 類模板的規則更簡單也更強大,例如允許遞歸和(成員)函數僅因返回類型不同而不同
  • 函數模板的規則允許編譯器從函數參數類型中找出模板參數

您可能可以使用函數模板重載來解決您的特定問題,但是您可能無法修復由於規則較長而熟悉其復雜性的人較少而出現的任何錯誤。 經過幾年的模板黑客攻擊,我並不知道微妙的函數模板重載是可能的。 在諸如 Boost 和 GCC 的 STL 之類的庫中,一種替代方法無處不在。 使用模板化包裝類:

template< typename X, typename Y >
struct functor {
    void operator()( X x, Y y );
};
template< typename X > // partial specialization: 
struct functor< X, int > { // alternative to overloading for classes
    void operator()( X x, int y );
};

現在您犧牲了隱式實例化語法(沒有尖括號)。 如果你想找回它,你需要另一個功能

template< typename X, typename Y > void func( X x, Y y ) {
    return functor< X, Y >()( x, y );
}

我很想知道函數重載是否可以做任何類 [部分] 專業化不能做的事情(除了推論)……

然后,當然,您的重載 #3 永遠不會面臨歧義,因為它的參數數量與任何其他重載不同。

我的立場更正 - 請參閱下面的評論。 我不會更改我的任何原始帖子,因為這會刪除回復的上下文。 我感謝評論者的意見,感謝他們的好意,不要投票給我


考慮模板就像宏預處理器,它在編譯器看到它們之前擴展 #defines。

編譯器將“擴展”您的模板參數,然后查看您的函數聲明。 所以,模板參數==函數參數。 如果你兩次聲明同一個函數,你會得到一個錯誤。

您詢問返回類型。 這是函數“簽名”的一部分。 兩個參數相同但返回類型不同的函數是兩個不同的函數。

除了評論之外,Herb Sutters 的文章Why Not Specialize Function Templates 中的主題的更多信息。 希望它也能有所幫助。

void fun(int i){
    cout<<i<<endl;
}

void fun(int i, string str){
    cout<<i<<str<<endl;
}

template <typename ... Args>
void sender(Args&& ... args)
{
    fun(forward<Args>(args) ...);
}

int main(){
    sender(5, "hello");
    sender(7);
}

暫無
暫無

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

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