2010-02-01 110 views
13

任何人都可以總結函數模板重載的想法嗎?重要的是,模板參數還是函數參數?什麼是回報價值?函數模板重載

例如,給出一個函數模板

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) {}; 
+13

所有那些尾隨的';'字符都是毫無意義的。它們不是必需的,實際上使它看起來更加混亂。 – Omnifarious 2010-02-01 04:53:12

回答

21

在該列表中,只有第二個引入了歧義,因爲函數 - 無論它們是否爲模板 - 都不能基於返回類型重載。

可以使用其他兩個:

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

將被使用,如果呼叫的第二個參數是一個整數,如func("string", 10);

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

會,如果你調用FUNC有三個使用參數。


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

14.5.5

A function template can be overloaded with other function templates and with normal (non-template) functions. A normal function is not related to a function template (i.e., it is never considered to be a specialization), even if it has the same name and type as a potentially generated function template specialization.)

非模板化(或 「少模板」)過載,優選的模板,例如

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,則將其從過載候選集合中刪除。

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

+0

感謝您的更正! – Potatoswatter 2010-02-01 18:38:27

+0

請注意,對於'template void func(T);模板 void func(T *);'也可以創建一個專門處理數組的版本:'template void func((T &)[N]);''這可以防止數組衰減成指針並調用指針的版本 – 2010-02-01 21:36:18

+0

我正在嘗試使用'template class_name :: class_name(const T&);'和'template class_name :: class_name(const T *);'來實例化一個類對象,但是當我通過一個類字符數組它嘗試調用'class_name(const T&)'而不是'class_name(const T *)'。我得到一個錯誤消息include/Class_name.h | 67 |錯誤:沒有匹配函數調用'to_string(const char [43])'|因爲'class_name(const T&)'我在'class_name(const T *)'中使用'to_string(T)''我特別處理char數組。 – 2017-07-19 03:59:32

1

我糾正 - 請參閱下面的評論。我不會更改我的任何原始帖子,因爲這會刪除回覆的上下文。我感謝提意見徵求他們的意見,併爲如此好心地不投我失望


考慮模板要像宏預處理器,編譯器能夠看到他們之前,其擴展的#define。

編譯器將「展開」您的模板參數,然後查看您的函數聲明。所以,模板參數==功能參數。如果你聲明兩次相同的函數,你會得到一個錯誤。

你問關於返回類型。這是該函數「簽名」的一部分。具有相同參數但返回類型不同的兩個函數是兩個不同的函數。

+4

非模板函數的返回類型不是其簽名的一部分(參見http://stackoverflow.com/questions/290038/is-the-return-type-part-of-the-function-signature/290048#290048 )。 – 2010-02-01 02:35:15

+1

與預處理器宏的比較是相當有缺陷的。有關重載的特殊規則。如果你有'template void foo(T);'和'void foo(int);',那麼像'foo(3);'這樣的調用肯定會解析到後者(如果匹配的話,非模板比模板更受歡迎究竟)。 – UncleBens 2010-02-01 08:09:24

+0

如果返回類型是您可以在逆向類型上重載的部分,那麼您不能使用 – 2010-02-01 09:50:24

3

這裏有兩個獨立的東西:函數模板和函數重載。任何兩個不同的模板聲明都可能是對方的重載,所以你的問題沒有如所述的那麼有意義。 (您給出的三個「過載」不是建立在第一個模板上,而是對同一個函數名稱有四個重載。)真正的問題是,如果調用一些過載(如),如何調用所需的過載?

首先,返回類型不參與重載過程,不管是否存在涉及的模板。所以#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將永遠不會面臨歧義,因爲它具有不同於任何其他超載的參數個數。

+0

+1適用於部分專業化的工作 – 2010-02-01 07:58:31

+0

您的示例似乎並不明確。該調用與第二個匹配(一個參數完全匹配int)。也許你的意思是重載:'template void func(int x,X y);模板 void func(X x,int y);' – UncleBens 2010-02-01 08:02:41

+0

第二個'func'將會拒絕第一個'func'接受的一些參數,但不會反過來,這會使模板更加專業化。對於一個普通的函數調用,如果我們忽略這個順序,調用將是不明確的,因爲模板參數同樣被推導爲'int'。 – 2010-02-01 08:55:51