如何重载std :: swap()

时间:2020-03-05 18:39:43  来源:igfitidea点击:

在排序甚至分配过程中,许多std容器(例如std :: list和std :: vector)都使用了std :: swap()。

但是swap()的std实现非常笼统,对于自定义类型而言效率很低。

因此,可以通过使用特定于自定义类型的实现重载std :: swap()来获得效率。但是如何实现它,以便将其用于std容器?

解决方案

回答

(按C ++标准)不允许我们重载std :: swap,但是特别允许我们将自己类型的模板特化添加到std名称空间。例如。

namespace std
{
    template<>
    void swap(my_type& lhs, my_type& rhs)
    {
       // ... blah
    }
}

那么在std容器(以及其他任何地方)中的用法将选择专业而不是一般的专业。

还要注意,为派生类型提供交换的基类实现还不够好。例如。如果你有

class Base
{
    // ... stuff ...
}
class Derived : public Base
{
    // ... stuff ...
}

namespace std
{
    template<>
    void swap(Base& lha, Base& rhs)
    {
       // ...
    }
}

这将适用于基类,但是如果我们尝试交换两个派生对象,它将使用std的通用版本,因为模板交换是完全匹配的(并且避免了仅交换派生对象的"基本"部分的问题)。

注意:我已经对其进行了更新,以删除上一个答案中的错误位。天哪! (感谢puetzk和j_random_hacker指出来)

回答

虽然通常不应该在std ::名称空间中添加内容是正确的,但明确允许为用户定义类型添加模板特化。没有重载功能。这是一个微妙的区别:-)

17.4.3.1/1
    It is undefined for a C++ program to add declarations or definitions
    to namespace std or namespaces with namespace std unless otherwise
    specified.  A program may add template specializations for any
    standard library template to namespace std. Such a specialization
    (complete or partial) of a standard library results in undefined
    behaviour unless the declaration depends on a user-defined name of
    external linkage and unless the template specialization meets the
    standard library requirements for the original template.

std :: swap的特殊化如下所示:

namespace std
{
    template<>
    void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}

没有template <>位,它将是一个未定义的重载,而不是允许的特殊化。 @Wilka提出的更改默认名称空间的建议方法可能与用户代码一起使用(由于Koenig查找更倾向于使用无名称空间的版本),但不能保证,而且实际上也并非如此(STL实现应使用完整的名称)。合格的std :: swap)。

在comp.lang.c ++。moded上有一个主题很长的讨论主题。不过,大多数内容都与部分专业化有关(目前尚无好方法)。

回答

重载交换的正确方法是将其写入与交换内容相同的名称空间中,以便可以通过依赖于参数的查找(ADL)找到它。一件特别容易做的事是:

class X
{
    // ...
    friend void swap(X& a, X& b)
    {
        using std::swap; // bring in swap for built-in types

        swap(a.base1, b.base1);
        swap(a.base2, b.base2);
        // ...
        swap(a.member1, b.member1);
        swap(a.member2, b.member2);
        // ...
    }
};