公众朋友交换成员函数
在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,提供一个在自由函数交换等方面的类内交换。但是这都是不必要的:任何正确使用交换将通过一个非限定的调用,我们的功能将通过ADL找到。 一个功能就可以做到。
我必须承认,和friend
我对“不友好”的条款有些不满。 所以,我的主要问题是:
swap
不是静态的 ? 它显然不使用任何成员变量。 friend
进来? 副题:
noexcept
标记我的swap
吗? friend iter begin()
和friend iter end()
放在一起吗? 我认为这里不需要friend
,对吧? 有几种写swap
,比其他方式更好一些。 但是,随着时间的推移,发现单一定义效果最佳。 让我们考虑一下如何考虑编写swap
函数。
我们首先看到像std::vector<>
这样的容器有一个单参数成员函数swap
,比如:
struct vector
{
void swap(vector&) { /* swap members */ }
};
当然,我们班也应该,对吧? 那么,不是真的。 标准库有各种不必要的东西,而成员swap
就是其中之一。 为什么? 我们继续。
我们应该做的是确定什么是规范的,以及我们的班级需要做什么来处理它。 交换的规范方法是使用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
}
}
那么在这种情况下肯定会起作用,但它有一个明显的问题:功能特化不能是局部的。 也就是说,我们不能用这个专门化模板类,只有特定的实例:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // 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找到; 如果你想将它作为一个自由函数,它需要被声明为如此(例如,参见这个例子)。 但是! 有一个问题。
如果你只是使用std::swap(x, y)
,你的重载将永远不会被发现,因为你已经明确地说过“在std
查找,而在其他地方”! 这就是为什么有些人建议编写两个函数:一个是通过ADL发现的函数,另一个是处理明确的std::
qualifications。
但就像我们看到的那样,这不能在所有情况下都起作用,而且我们最终会遇到一个丑陋的混乱。 相反,习惯性交换走向了另一条路线:与上面提到的不同,它不是将类作为提供std::swap
,而是std::swap
者的工作,以确保它们不使用合格的swap
。 只要人们知道这一点,这种方法就会工作得很好。 但问题在于:需要使用不合格的呼叫是不直观的!
为了方便起见,一些库如Boost提供了函数boost::swap
,它只是一个非限定的swap
调用, std::swap
作为关联的名称空间。 这有助于使事情再简洁一些,但它仍然是一个无赖。
请注意,C ++ 11对std::swap
的行为没有任何改变,我和其他人错误地认为是这种情况。 如果你有点这样,请阅读这里。
总之:会员功能只是噪音,专业化是丑陋的,不完整的,但friend
功能齐全,有效。 当你交换时,或者使用boost::swap
或者与std::swap
关联的非限定swap
。
†非正式地,如果名称在函数调用期间被考虑,则该名称是相关联的。 有关详细信息,请阅读第3.4.2节。 在这种情况下,通常不会考虑std::swap
; 但我们可以将其关联(将其添加到由不合格swap
考虑的重载集合),以便找到它。
该代码相当于(几乎在任何情况下):
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),该函数名称是非限定的,并且该函数具有命名空间范围时,可以在类的朋友声明中定义函数。
这样的功能隐式内联。 在类中定义的朋友函数位于定义它的类的(词法)范围内。 在课堂外定义的朋友函数不是。
链接地址: http://www.djcxy.com/p/12675.html