您是否应该更喜欢重载而不是函数模板的专门化?

问题描述 投票:0回答:3

我有一本教科书指出,您可以通过模板专业化或函数重载为

swap(x,y)
等标准库函数提供自己的实现。这对于任何可以从赋值交换以外的东西中受益的类型都很有用,例如 STL 容器(我知道,它已经编写了交换)。

我的问题如下:

  1. 更好的是:模板专业化为您提供专业化 交换实现或函数重载提供了确切的 您希望在没有模板的情况下使用参数吗?

  2. 为什么更好?或者如果它们相等,这是为什么?

c++ stl template-specialization standard-library overloading
3个回答
105
投票

短篇故事:可以的时候超载,需要的时候专业化。

长话短说: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
命名空间中!

此时您有两个选择:

  1. 在您自己的命名空间中创建一个

    swap(Foo<T>&, Foo<T>&)
    函数,并希望通过 ADL 找到它。我说“希望”是因为如果标准库像
    std::swap(a, b);
    那样调用 swap,那么 ADL 根本无法工作。

  2. 忽略标准中规定不要添加重载的部分,无论如何都要这样做。老实说,尽管技术上不允许,但在所有现实场景中它都会起作用。

要记住的一件事是,根本不能保证标准库使用

swap
。大多数算法都使用
std::iter_swap
,并且在我研究过的一些实现中,它并不总是转发到
std::swap


18
投票

彼得·亚历山大的回答没什么可补充的。让我提一下函数专业化中的一个用途可能比重载更可取:如果您必须在没有参数的函数中进行选择

例如

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; }

8
投票

不允许您重载

std
命名空间中的函数,但允许您专门化模板(据我记得),所以这是一种选择。

另一种选择是将您的

swap
函数与其所操作的对象放在同一命名空间中,并在调用不合格的交换之前
using std::swap;

© www.soinside.com 2019 - 2024. All rights reserved.