C++ 如何重载std::swap()
C++ 如何重载std::swap(),c++,performance,optimization,stl,c++-faq,C++,Performance,Optimization,Stl,C++ Faq,std::swap()被许多std容器(例如std::list和std::vector)在排序和均匀分配期间使用 但是swap()的std实现非常通用,对于自定义类型来说效率相当低 因此,可以通过使用自定义类型特定的实现重载std::swap()来获得效率。但是,如何实现它,以便它将被STD容器使用?< P>您不允许(由C++标准)重载STD::SWAP,但是您被特别地允许将您自己的类型的模板专长添加到STD命名空间。例如 namespace std { template<>
std::swap()
被许多std容器(例如std::list
和std::vector
)在排序和均匀分配期间使用
但是swap()
的std实现非常通用,对于自定义类型来说效率相当低
因此,可以通过使用自定义类型特定的实现重载std::swap()
来获得效率。但是,如何实现它,以便它将被STD容器使用?< P>您不允许(由C++标准)重载STD::SWAP,但是您被特别地允许将您自己的类型的模板专长添加到STD命名空间。例如
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}
名称空间std
{
模板
无效掉期(my_类型和lhs、my_类型和rhs)
{
//……废话
}
}
然后std容器(以及其他任何地方)中的用法将选择您的专业化,而不是一般的专业化
还要注意,提供swap的基类实现对于派生类型来说还不够好。如果你有
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}
类基
{
//…东西。。。
}
派生类:公共基
{
//…东西。。。
}
名称空间标准
{
模板
无效掉期(基础和lha、基础和rhs)
{
// ...
}
}
这将适用于基类,但如果您尝试交换两个派生对象,它将使用std的通用版本,因为模板交换是完全匹配的(并且它避免了仅交换派生对象的“基本”部分的问题)
注意:我已经更新了这个,删除了我上一个答案中的错误部分。哦!(感谢puetzk和j_random_hacker指出这一点)虽然通常不应该向std::namespace添加内容是正确的,但特别允许为用户定义的类型添加模板专门化。重载函数是不正确的。这是一个微妙的区别:-) 17.4.3.1/1 对于C++程序来说,添加声明或定义是未定义的。 除非另有说明,否则使用名称空间std或名称空间std 明确规定。程序可以为任何对象添加模板专门化 命名空间标准的标准库模板。这样的专门化 标准库的(全部或部分)导致未定义 行为,除非声明依赖于用户定义的 外部链接,除非模板专门化满足 原始模板的标准库要求 std::swap的专门化如下所示:
namespace std
{
template<>
void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}
名称空间std
{
模板
无效交换(myspace::mytype&a,myspace::mytype&b){…}
}
如果没有模板位,它将是一个未定义的重载,而不是一个允许的专门化@Wilka建议的更改默认名称空间的方法可能适用于用户代码(因为Koenig lookup更喜欢无名称空间的版本),但不能保证,事实上也不应该这样做(STL实现应该使用完全限定的std::swap)
关于这个话题有一个长的讨论。不过,大部分内容都是关于部分专门化的(目前还没有好的方法来实现)。重载交换的正确方法是将其写入与交换内容相同的命名空间中,以便通过找到它。一件特别容易的事情是:
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);
// ...
}
};
注意Mozza314 下面是一个通用
std::algorithm
调用std::swap
,并让用户在名称空间std中提供其交换的效果模拟。由于这是一个实验,此模拟使用名称空间exp
而不是名称空间std
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
exp::swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
namespace exp
{
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
如果您的编译器打印出不同的内容,那么它没有正确地实现模板的“两阶段查找”
如果您的编译器符合(C++98/03/11中的任何一个),那么它将给出与我显示的相同的输出。在这种情况下,你所担心的会发生,确实会发生。将swap
放入命名空间std
(exp
)并没有阻止它的发生
Dave和我都是委员会成员,十年来一直在这个标准领域工作(但并不总是彼此一致)。但这个问题已经解决了很长一段时间,我们都同意如何解决这个问题。忽视Dave在这方面的专家意见/答案,后果自负
这一问题是在C++98出版后才被发现的。大约从2001年开始,戴夫和我开始。这就是现代解决方案:
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
void swap(A&, A&)
{
printf("swap(A, A)\n");
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
更新
有人指出:
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
现在它不再工作了-(
所以你可以把
swap
放在namespace std中并让它工作。但是当你有一个模板:A
时,你需要记住把swap
放在A
的名称空间中,因为这两种情况都可以工作,所以更容易记住(并教导他人)只需这样做。在C++2003中,它最多是未指定的。大多数实现确实使用ADL来查找交换,但没有强制要求,因此您不能指望它。您可以为特定的具体类型专门化std::swap,如OP所示;只是不要期望专门化会被使用,例如用于该类型的派生类。我会惊奇地发现,实现仍然没有使用ADL来找到正确的交换。这是委员会的一个老问题。如果您的实现没有使用ADL来找到交换,请提交一份错误报告。@Sascha:首先,我在名称空间范围内定义函数,因为这是对泛型代码唯一重要的定义。因为int et。al.没有/不能有成员函数,std::sort等必须使用自由函数;它们建立协议。其次,我不知道为什么你反对有两个实现,但如果你不能接受有一个非memb,大多数类注定会被无效地排序
swap(A, A)
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// simulate user code which includes <algorithm>
template <class T>
struct A
{
};
namespace exp
{
template <class T>
void swap(A<T>&, A<T>&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A<int> a[2];
exp::algorithm(a, a+2);
}