作者:小乌龟和小土鳖 | 来源:互联网 | 2023-09-16 18:17
有几种写法swap
,有些比其他更好。然而,随着时间的推移,人们发现单一的定义效果最好。让我们考虑一下如何考虑编写一个swap
函数。
我们首先看到容器 likestd::vector<>
有一个单参数的成员函数swap
,例如:
struct vector
{
void swap(vector&) { /* swap members */ }
};
那么,当然,我们班也应该,对吧?嗯,不是真的。标准库有各种不必要的东西,成员swap
就是其中之一。为什么?我们继续。
我们应该做的是确定什么是规范的,以及我们的班级 需要 做什么才能使用它。交换的规范方法是 withstd::swap
。这就是为什么成员函数没有用的原因:它们不是我们应该交换事物的方式,一般来说,并且与std::swap
.
那么,为了std::swap
工作,我们应该提供(并且std::vector<>
应该提供) 的专业化std::swap
,对吗?
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
好吧,这在这种情况下肯定会起作用,但它有一个明显的问题:函数特化不能是局部的。也就是说,我们不能用这个专门化模板类,只有特定的实例化:
namespace std
{
template
void swap(myclass&, myclass&) // error! no partial specialization
{
// swap
}
}
这种方法在某些时候有效,但并非所有时候都有效。一定会有更好的办法。
有!我们可以使用一个friend
函数,并通过ADL找到它:
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
当我们想交换一些东西时,我们关联——然后进行无条件的调用: std::swap
using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first
// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap
什么是friend
函数?这个地区有混乱。
在 C++ 标准化之前,friend
函数会做一些称为“朋友名称注入”的事情,其中代码的行为 就
好像函数是在周围的命名空间中编写的一样。例如,这些是等效的预标准:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
然而,当ADL被发明时,它被删除了。friend
然后只能 通过 ADL 找到该函数;如果您希望它是一个自由函数,则需要将其声明为这样(例如,参见
this)。但是看!有问题。
如果你只使用std::swap(x, y)
,你的重载将 永远不会
被发现,因为你已经明确说过“看看std
,别无他处”!这就是为什么有些人建议编写两个函数:一个作为通过ADL找到的函数,另一个用于处理显式std::
限定。
但就像我们看到的那样,这不能在所有情况下都有效,我们最终会陷入丑陋的混乱局面。相反,惯用的交换走的是另一条路:不是让类的工作来提供std::swap
,而是交换者的工作是确保他们不使用qualifiedswap
,就像上面一样。只要人们知道,这往往效果很好。但问题就在这里:需要使用不合格的调用是不直观的!
为了使这更容易,像 Boost 这样的一些库提供了函数boost::swap
,它只是对
进行非限定调用swap
,并std::swap
作为关联的命名空间。这有助于使事情再次简洁,但它仍然是一个无赖。
请注意,C++11 中
的行为没有变化std::swap
,我和其他人错误地认为是这种情况。
简而言之:成员函数只是噪音,特化丑陋且不完整,但friend
功能完整且有效。而当你交换时,要么使用boost::swap
或不合格swap
的
withstd::swap
关联。
‘ 通俗地说,如果在函数调用期间将考虑名称,则将其 关联。 详情请阅读搂3.4.2。在这种情况下,std::swap
通常不考虑;但我们可以 关联
它(将其添加到 unqualified 考虑的重载集合中swap
),以便找到它。