公众朋友交换成员函数
在复制和交换成语的美丽答案有一段代码,我需要一点帮助:
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); } // ... };
他又加了一张纸条
还有其他的说法,我们应该专门为我们的typesstd :: swap,提供一个在一个自由function交换,一个类交换,但这是不必要的:任何正确使用交换将通过一个非限定的调用,我们的function将通过ADLfind。 一个function就可以了。
和friend
我有点“不友好”的说法,我必须承认。 所以,我的主要问题是:
- 看起来像一个免费的function ,但它的内部类体呢?
- 为什么不是这个
swap
静态 ? 它显然不使用任何成员variables。 - “任何适当的交换使用将通过ADLfind交换” ? ADL将search命名空间,对吗? 但是,它也看内部类? 或者在这里
friend
进来?
副题:
- 用C ++ 11,我应该用
noexcept
标记我的swap
吗? - 用C ++ 11和它的范围 ,我应该把
friend iter begin()
放在friend iter begin()
和friend iter end()
? 我觉得这里不需要friend
,对吧?
有几种写swap
,比其他的更好一些。 但是,随着时间的推移,发现一个单一的定义效果最好。 让我们考虑一下如何考虑编写一个swap
函数。
我们首先看到像std::vector<>
这样的容器有一个单参数成员函数swap
,比如:
struct vector { void swap(vector&) { /* swap members */ } };
当然,我们class也应该,对吧? 那么,不是真的。 标准库有各种不必要的东西 ,而成员swap
就是其中之一。 为什么? 我们继续。
我们应该做的是确定什么是经典的,以及我们的class级需要做什么来处理它。 而交换的规范方法是使用std::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 } }
那么这肯定会在这种情况下工作,但它有一个明显的问题:function专业化不能是局部的。 也就是说,我们不能用这个专门化模板类,只有特定的实例:
namespace std { template <typename T> void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization { // swap } }
这种方法在某些时候有效,但不是全部。 一定会有更好的办法。
有! 我们可以使用friend
function,并通过ADLfind它:
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
function? 这个地区有混乱。
在C ++被标准化之前, friend
函数做了一个叫做“朋友名注入”的东西,代码的行为就好像函数是否写在了周围的名字空间中一样。 例如,这些是等同的标准:
struct foo { friend void bar() { // baz } }; // turned into, pre-standard: struct foo { friend void bar(); }; void bar() { // baz }
但是,当ADL被发明时,这被删除。 friend
函数只能通过ADLfind; 如果你想要它作为一个自由的function,它需要被声明如此(例如见这个 )。 但是! 有一个问题。
如果你只是使用std::swap(x, y)
,那么你的重载将永远不会被find,因为你已经明确地说过“在std
查找,而在其他地方”! 这就是为什么有些人build议编写两个函数:一个是通过ADLfind的函数,另一个是处理明确的std::
qualifications。
但就像我们所看到的那样,这不可能在所有情况下都起作用,而且我们最终会变得丑陋。 相反,习惯交换是另外一条路线:不是将类作为提供std::swap
,而是std::swap
者的工作,以确保它们不使用像上面那样的合格swap
。 而且,只要人们知道这一点,这个工作就会很顺利。 但问题在于:需要使用不合格的电话是不直观的。
为了方便起见,一些类似Boost的库提供了boost::swap
函数,它只是一个非限定的swap
调用, std::swap
作为一个关联的名字空间。 这有助于使事情简洁一些,但它仍然是一个无赖。
请注意,C ++ 11对std::swap
的行为没有任何改变,我和其他人错误地认为是这种情况。 如果你有点这个, 请阅读这里 。
总之:会员function只是噪音,专业化是丑陋的,不完整的,但friend
function是完整的,有效的。 而当你交换,要么使用boost::swap
或与std::swap
关联的非限定swap
。
†非正式地,如果在函数调用期间将会考虑一个名字,则这个名字是相关联的 。 有关详细信息,请阅读第3.4.2节。 在这种情况下,通常不考虑std::swap
; 但是我们可以把它关联起来(把它添加到由不合格的swap
考虑的重载集合中),从而可以find它。
该代码相当于( 几乎在任何情况下):
class dumb_array { public: // ... friend void swap(dumb_array& first, dumb_array& second); // ... }; inline void swap(dumb_array& first, dumb_array& second) // nothrow { using std::swap; swap(first.mSize, second.mSize); swap(first.mArray, second.mArray); }
一个类中定义的朋友函数是:
- 放置在封闭的命名空间中
- 自动
inline
- 能够在没有进一步资格的情况下引用该课程的静态成员
确切的规则在[class.friend]
部分(我引用C ++ 0x草案的第6和7段):
函数可以定义在一个类的朋友声明中,当且仅当该类是一个非本地类(9.8),函数名是不合格的,函数具有命名空间范围。
这样的function隐式内联。 在一个类中定义的朋友函数在它定义的类的(词法)范围内。 在类外定义的朋友函数不是。