我已经注意到一本教科书,您可以通过模板专业化为函数重载提供标准库函数(如swap(x,y)
)的自己的实现。这对于可以从赋值交换以外的其他东西中受益的任何类型都是有用的,例如STL containers
(我知道已经编写了交换)。
我的问题是:
什么更好:模板专业化,以提供您的专业 交换实现,或提供精确的函数重载 您希望在没有模板的情况下使用的参数?
为什么更好?或者如果他们是平等的,为什么会这样?
答案 0 :(得分:67)
短篇小说:尽可能超载,专注于你需要的时候。
长篇小说: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
类型,并且它有一个高性能交换,然后你只需要在swap(Foo&, Foo&)
命名空间中专门化std
。没问题。
但如果Foo
是模板类怎么办? C ++没有函数的部分特化,因此你不能专门化swap
。您唯一的选择是重载,但标准规定您不允许在std
命名空间中添加重载!
此时您有两种选择:
在您自己的命名空间中创建一个swap(Foo<T>&, Foo<T>&)
函数,并希望通过ADL找到它。我说“希望”,因为如果标准库调用像std::swap(a, b);
这样的交换,那么ADL就行不通。
忽略标准中没有添加重载的部分,无论如何都要这样做。老实说,即使技术上不允许,但在所有现实场景中它都会起作用。
要记住的一件事是,无法保证标准库完全使用swap
。大多数算法使用std::iter_swap
,在我看过的一些实现中,它并不总是转发到std::swap
。
答案 1 :(得分:12)
彼得亚历山大的答案几乎没有什么可补充的。让我简单地提一下使用函数专业化可能比重载更具优势:如果你必须在没有参数的函数中选择。
E.g。
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; }
答案 2 :(得分:5)
您不能重载std
命名空间中的函数,但您可以专门化模板(我记得),这是一个选项。
另一种选择是在调用非限定交换之前,将swap
函数放在与其运行的函数相同的名称空间中,并using std::swap;
。