教材我注意到,您可以通過模板專門化爲函數重載提供您自己的標準庫函數實現,如swap(x,y)
。這對任何可以從除賦值轉換外的其他類型受益的類型都很有用,例如STL containers
(已知有交換,我知道)。模板專精VS函數重載
我的問題是:
更棒的是:模板專門給你的專業 交換實現,或者函數重載提供你希望沒有模板的確切 參數?
爲什麼它更好?或者如果他們是平等的,爲什麼呢?
教材我注意到,您可以通過模板專門化爲函數重載提供您自己的標準庫函數實現,如swap(x,y)
。這對任何可以從除賦值轉換外的其他類型受益的類型都很有用,例如STL containers
(已知有交換,我知道)。模板專精VS函數重載
我的問題是:
更棒的是:模板專門給你的專業 交換實現,或者函數重載提供你希望沒有模板的確切 參數?
爲什麼它更好?或者如果他們是平等的,爲什麼呢?
小故事:當你可以超載時,專門在你需要的時候。長篇小說:C++對待專業化和重載的方式非常不同。這可以用一個例子來解釋。
template <typename T> void foo(T);
template <typename T> void foo(T*); // overload of foo(T)
template <> void foo<int>(int*); // specialisation of foo(T*)
foo(new int); // calls foo<int>(int*);
現在讓我們換掉最後兩個。
template <typename T> void foo(T);
template <> void foo<int*>(int*); // specialisation of foo(T)
template <typename T> void foo(T*); // overload of foo(T)
foo(new int); // calls foo(T*) !!!
編譯器在重載分辨率之前甚至查看專業化。所以,在這兩種情況下,重載解決方案都會選擇foo(T*)
。但是,只有在第一種情況下才會發現foo<int*>(int*)
,因爲在第二種情況下,int*
專業化是foo(T)
的專門化,而不是foo(T*)
。
您提到過std::swap
。這使事情變得更加複雜。
該標準說您可以將專門化添加到std
命名空間。太棒了,所以你有一些Foo
類型,它有一個高性能的交換,那麼你只需在std
命名空間中專門設置swap(Foo&, Foo&)
。沒問題。
但是,如果Foo
是模板類呢? C++沒有部分功能的專業化,所以你不能專注於swap
。你唯一的選擇是超載,但標準說你不允許在std
命名空間中添加重載!
你必須在這一點上兩個選項:
創建自己的命名空間的swap(Foo<T>&, Foo<T>&)
功能,並希望它得到通過ADL找到。我說「希望」,因爲如果標準庫調用像std::swap(a, b);
那樣的交換,那麼ADL根本無法工作。
忽略標準中說不添加重載的部分,並且無論如何都要這樣做。老實說,即使它在技術上是不允許的,但在所有現實的情況下,它都會起作用。
但要記住的一件事是,不能保證標準庫完全可以使用swap
。大多數算法使用std::iter_swap
,在我看過的一些實現中,它並不總是轉發到std::swap
。
您不允許在std
命名空間中重載函數,但允許您專門化模板(我記得),所以這是一種選擇。
另一種選擇是在調用非限定交換之前,將swap
函數放在與其運行的函數相同的名稱空間中,並將其與using std::swap;
相同。
彼得亞歷山大的答案几乎沒有添加。讓我僅僅提到一個使用這個功能的專業化可能會優於超載:如果您必須在沒有參數的函數中選擇。
例如
template<class T> T zero();
template<> int zero() { return 0; }
template<> long zero() { return 0L; }
要使用函數重載做同樣的事情,你將有一個參數添加到函數簽名:
int zero(int) { return 0; }
long zero(long) { return 0L; }
有趣的用法:) –
很好的例子先生 – tenfour
「我說希望,因爲」 ...這一點指出在幾年前的WG21中。所有標準庫實現者都不知道。 – MSalters
精彩的回答,非常感謝。 –