在copy-and-swap-idiom的漂亮答案中,有一段代码我需要一些帮助:
class dumb_array
{
public:
// ...
friend void swap(dumb_array& first, dumb_array& second) // nothrow
{
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
}
// ...
};
他补充说明
还有其他声称我们应该专门为我们的类型使用std :: swap,提供一个类内交换以及一个自由函数交换等等 . 但这都是不必要的:任何正确使用swap都将通过不合格调用,我们的功能将通过ADL找到 . 一个功能就可以了 .
我必须承认, friend
我有点"unfriendly"条款 . 所以,我的主要问题是:
-
looks like a free function ,但它在 class 体内吗?
-
why isn't this swap static ?它显然不使用任何成员变量 .
-
"Any proper use of swap will find out swap via ADL" ? ADL会搜索命名空间,对吧?但它也看到了课程内部吗?或者
friend
在哪里?
副题:
-
使用C 11,我应该用 noexcept 标记
swap
吗? -
使用C 11及其 range-for ,我应该在课程中以相同的方式放置
friend iter begin()
和friend iter end()
吗?我觉得这里不需要friend
对吧?
2 回答
有几种方法可以写
swap
,有些方法比其他方法更好 . 但是,随着时间的推移,发现单一定义效果最好 . 让我们考虑如何考虑编写swap
函数 .我们首先看到像
std::vector<>
这样的容器有一个单参数成员函数swap
,例如:当然,我们的 class 也应该,对吧?嗯,不是真的 . 标准库有all sorts of unnecessary things,成员
swap
就是其中之一 . 为什么?我们继续 .我们应该做的是确定什么是规范的,以及我们的课程需要做些什么才能使用它 . 规范的交换方法是
std::swap
. 这就是为什么成员函数通常不应该如何交换东西,而且与std::swap
的行为无关 .那么,为了使
std::swap
工作,我们应该提供(并且std::vector<>
应该提供)std::swap
的专业化,对吧?那么这肯定会在这种情况下起作用,但它有一个明显的问题:功能专业化不能是局部的 . 也就是说,我们不能用这个来专门化模板类,只有特定的实例化:
这种方法在某些时候有效,但不是所有时间都有效 . 肯定有更好的办法 .
有!我们可以使用
friend
函数,并通过ADL找到它:当我们要交换某些内容时,我们会关联†
std::swap
然后进行无条件的通话:什么是
friend
功能?这个地区有混乱 .在C标准化之前,
friend
函数执行了一个名为"friend name injection"的函数,其中代码的行为就好像函数是在周围的命名空间中编写的一样 . 例如,这些是等效的预标准:然而,当发明ADL时,这被删除了 . 然后只能通过ADL找到
friend
函数;如果你想把它作为一个自由函数,它需要被声明为(例如see this) . 但是,瞧!有一个问题 .如果你只是使用
std::swap(x, y)
,你的重载将永远不会被发现,因为你明确地说过“查看std
,而不是其他地方”!这就是为什么有些人建议编写两个函数的原因:一个是通过ADL找到的函数,另一个是处理显式的std::
资格 .但是就像我们看到的那样,这可以't work in all cases, and we end up with an ugly mess. Instead, idiomatic swapping went the other route: instead of making it the classes'工作提供
std::swap
,它's the swappers'工作以确保他们不使用合格的swap
,就像上面一样 . 只要人们了解它,这往往效果很好 . 但其中存在的问题是:需要使用不合格的电话是不直观的!为了使这更容易,像Boost这样的库提供了函数
boost::swap
,它只对swap
进行了非限定调用,std::swap
作为关联的命名空间 . 这有助于使事情再次简洁,但它仍然是一个无赖 .请注意,C11对
std::swap
的行为没有任何变化,我和其他人错误地认为是这种情况 . 如果你有点,read here .简而言之:成员函数只是噪声,专业化是丑陋和不完整的,但
friend
函数是完整的并且有效 . 当你交换时,使用boost::swap
或不合格的swap
与std::swap
相关联 .†非正式地,如果在函数调用期间考虑名称,则关联名称 . 有关详细信息,请阅读§3.4.2 . 在这种情况下,通常不考虑
std::swap
;但是我们可以将它关联起来(将它添加到由不合格的swap
考虑的重载集),允许它被找到 .该代码相当于(几乎在所有方面):
在类中定义的友元函数是:
放在封闭的命名空间中
自动
inline
能够在没有进一步限定的情况下引用该类的静态成员
确切的规则在
[class.friend]
部分(我引用C 0x草案的第6和第7段):