tow*_*owi 154 c++ friend copy-and-swap c++11
在复制和交换习语的漂亮答案中,有一段代码我需要一些帮助:
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);
}
// ...
};
Run Code Online (Sandbox Code Playgroud)
他补充说明
还有其他声称我们应该专门为我们的类型使用std :: swap,提供一个类内交换以及一个自由函数交换等等.但这都是不必要的:任何正确使用swap都将通过一个不合格的调用,我们的功能将通过ADL找到.一个功能就可以了.
随着friend我对"不友好"的条款一点,我必须承认.所以,我的主要问题是:
swap静态的?它显然不使用任何成员变量.friend进来?副题:
swaps的noexcept?friend iter begin()和friend iter end()在类中以同样的方式?我觉得friend这里不需要,对吗?GMa*_*ckG 161
有几种方法可以写swap,有些方法比其他方法更好.但是,随着时间的推移,发现单个定义效果最好.让我们考虑如何考虑编写swap函数.
我们首先看到容器就像std::vector<>具有单参数成员函数swap,例如:
struct vector
{
void swap(vector&) { /* swap members */ }
};
Run Code Online (Sandbox Code Playgroud)
当然,我们的班级也应该,对吧?好吧,不是真的.标准库有各种不必要的东西,成员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
}
}
Run Code Online (Sandbox Code Playgroud)
那么这肯定会在这种情况下起作用,但它有一个明显的问题:功能专业化不能是局部的.也就是说,我们不能用这个来专门化模板类,只有特定的实例化:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法在某些时候有效,但不是所有时间都有效.肯定有更好的办法.
有!我们可以使用一个friend函数,并通过ADL找到它:
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
Run Code Online (Sandbox Code Playgroud)
当我们要交换某些内容时,我们会关联† 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
Run Code Online (Sandbox Code Playgroud)
什么是friend功能?这个地区有混乱.
在C++标准化之前,friend函数执行了一个名为"朋友名称注入"的操作,其中代码的行为就好像函数是在周围的命名空间中编写的一样.例如,这些是相同的预标准:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
Run Code Online (Sandbox Code Playgroud)
然而,当发明ADL时,这被删除了.friend然后只能通过ADL找到该功能; 如果你想将它作为一个自由函数,它需要被声明为(例如,见这个).但是,瞧!有一个问题.
如果你只是使用std::swap(x, y),你的过载将永远不会被发现,因为你已经明确地说"看看std,而不是其他地方"!这就是为什么有些人建议写两个函数:一个是通过ADL找到的函数,另一个是处理显式std::资格.
但就像我们所看到的那样,这在所有情况下都无法奏效,而且最终会出现一个丑陋的混乱局面.相反,惯用交换是另一条路线:不是让它成为班级提供的工作std::swap,而是交换者的工作,以确保他们不使用合格的swap,如上所述.只要人们了解它,这往往效果很好.但其中存在的问题是:需要使用不合格的电话是不直观的!
为了更方便,像加速一些库提供的功能boost::swap,这只是做一个不合格的呼叫swap,以std::swap作为相关的命名空间.这有助于使事情再次简洁,但它仍然是一个无赖.
请注意,C++ 11中的行为没有变化std::swap,我和其他人错误地认为是这种情况.如果您对此感到满意,请阅读此处.
简而言之:成员函数只是噪声,专业化是丑陋和不完整的,但friend功能是完整和有效的.当你交换时,要么使用,要么使用boost::swap不合格swap的std::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);
}
Run Code Online (Sandbox Code Playgroud)
在类中定义的友元函数是:
inline确切的规则在章节中[class.friend](我引用C++ 0x草案的第6和第7段):
当且仅当该类是非本地类(9.8),函数名称是非限定的,并且该函数具有命名空间范围时,才能在类的友元声明中定义函数.
这样的功能是隐含的内联.在类中定义的友元函数位于定义它的类的(词法)范围内.在课外定义的朋友函数不是.
| 归档时间: |
|
| 查看次数: |
24905 次 |
| 最近记录: |