公共朋友交换成员函数 [英] public friend swap member function
问题描述
在对复制和交换惯例的美丽答案中有一段代码我需要一点帮助:
class dumb_array
{
public:
// ...
friend void swap(dumb_array& first,dumb_array& second)// nothrow
{
使用std :: swap;
swap(first.mSize,second.mSize);
swap(first.mArray,second.mArray);
}
// ...
};
并且他添加了一个注释
还有其他声明,我们应该专门化std :: swap为我们的类型,提供一个类内交换自由功能交换等。但这是没有必要的:任何正确使用交换将通过一个无限制的调用,我们的功能将通过ADL找到。一个函数会做。
有朋友
我有点不友好条件,我必须承认。所以,我的主要问题是:
- 看起来像一个自由函数 / li>
- 为什么不是
swap
static ?它显然不使用任何成员变量。 - 任何正确使用swap会发现通过ADL交换? ADL将搜索命名空间,对吗?但是它也看看内部类? c> c> p>
- 对于C ++ 11,我应该使用标记
swap
code> noexcept ? - 使用C ++ 11及其
friend iter begin()
和friend iter end()
我认为这里不需要朋友
?
有几种方法来写 swap
,有些比其他更好。然而,随着时间的推移,发现一个单一的定义效果最好。让我们考虑如何写一个 swap
函数。
我们首先看到 std :: vector<>
的容器有一个单参数成员函数 swap
as:
struct vector
{
void swap(vector&){/ * swap members * }
};
当然,我们的类应该也是吧?嗯,不是真的。标准库具有各种不必要的功能,以及成员 swap
是其中之一。为什么?让我们继续。
我们应该做的是识别什么是标准,以及我们的类 >做与它合作。而交换的规范方法是使用 那么,为了使 在这种情况下肯定会工作,但它有一个明显的问题:专业化不能是部分的。也就是说,我们不能将模板类专门化为这个,只有特定的实例化: 这种方法在某些时候工作,但不是所有的时间。必须有更好的方法。 有!我们可以使用 当我们想交换东西时,我们将† 什么是 在C ++标准化之前, std :: swap
。这就是为什么成员函数不是有用的:它们不是我们应该如何交换东西,一般来说,并且对 std :: swap
的行为没有影响。 / p>
std :: swap
工作,我们应该提供(和 std: :vector<>
应该提供了 std :: swap
的专业化,对吧?
命名空间std
{
template<> // important! std的特殊化是好的,重载是UB
void swap(myclass& myclass&)
{
// swap
}
}
命名空间std
{
template< typename T>
void swap< T>(myclass< T& amp; myclass< T&)//错误!无部分专业化
{
// swap
}
}
$ b b
朋友
函数,并通过ADL找到它:
namespace xyz
{
struct myclass
{
friend void swap(myclass& myclass&);
};
}
std :: swap
然后进行无限制调用:
std :: swap; //允许使用std :: swap ...
swap(x,y); // ...但是选择重载,首先
//这就是说,如果swap(x,y)找到更好的匹配,通过ADL,它
//将使用它;否则它会回到std :: swap
code> function?
friend
函数做了一些名为friend name injection ,其中代码表现为如果
struct foo
{
friend void bar
{
// baz
}
};
//转为,预标准:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
然而,当ADL被发明时,这被删除。然后可以通过ADL找到 friend
函数 如果你想要它作为一个自由函数,它需要被声明为这样(查看此,例如)。但是!有一个问题。
如果你只是使用 std :: swap(x,y)
,因为你已经明确地说:在 std
中,而不在其他地方!这是为什么有些人建议写两个函数:一个作为通过ADL找到的函数,另一个处理显式 std ::
资格。
但是就像我们看到的,这在所有情况下都不能工作,我们最终陷入了一个丑陋的混乱。相反,习惯交换是另一条路线:而不是让类的工作提供 std :: swap
,这是交换器的工作,以确保他们不使用合格交换
,如上所示。而这往往工作得很好,只要人们知道它。
为了使这更容易,一些库像Boost提供的函数 boost :: swap
,它只是对 swap
进行了无限制的调用, std :: swap
作为关联的命名空间。
请注意,C ++ 11没有改变 std的行为:: swap
,我和其他人错误地认为是这种情况。如果你这么做,请在这里阅读。
简而言之:成员函数只是噪声,专业化是丑陋和不完整的,但 friend
函数是完整的和工作。当您交换时,使用 boost :: swap
或不合格交换
与 std: :swap
关联。
†非正式地,名称是 >如果它将在函数调用期间被考虑。详情请参阅§3.4.2。在这种情况下,通常不会考虑 std :: swap
;但我们可以关联(将其添加到由不合格 swap
考虑的重载集合中),允许找到它。
In the beautiful answer to the copy-and-swap-idiom there is a piece of code I need a bit of help:
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);
}
// ...
};
and he adds a note
There are other claims that we should specialize std::swap for our type, provide an in-class swap along-side a free-function swap, etc. But this is all unnecessary: any proper use of swap will be through an unqualified call, and our function will be found through ADL. One function will do.
With friend
I am a bit on "unfriendly" terms, I must admit. So, my main questions are:
- looks like a free function, but its inside the class body?
- why isn't this
swap
static? It obviously doesn't use any member variables. - "Any proper use of swap will find out swap via ADL"? ADL will search the namespaces, right? But does it also look inside classes? Or is here where
friend
comes in?
Side-questions:
- With C++11, should I mark my
swap
s withnoexcept
? - With C++11 and its range-for, should I place
friend iter begin()
andfriend iter end()
the same way inside the class? I think thefriend
is not needed here, right?
There are several ways to write swap
, some better than others. Over time, though, it was found a single definition works best. Let's consider how we might think about writing a swap
function.
We first see that containers like std::vector<>
have a single-argument member function swap
, such as:
struct vector
{
void swap(vector&) { /* swap members */ }
};
Naturally, then, our class should too, right? Well, not really. The standard library has all sorts of unnecessary things, and a member swap
is one of them. Why? Let's go on.
What we should do is identify what's canonical, and what our class needs to do to work with it. And the canonical method of swapping is with std::swap
. This is why member functions aren't useful: they aren't how we should swap things, in general, and have no bearing on the behavior of std::swap
.
Well then, to make std::swap
work we should provide (and std::vector<>
should have provided) a specialization of std::swap
, right?
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
Well that would certainly work in this case, but it has a glaring problem: specializations cannot be partial. That is, we cannot specialize template classes with this, only particular instantiations:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
This method works some of the time, but not all of the time. There must be a better way.
There is! We can use a friend
function, and find it through ADL:
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
When we want to swap something, we associate† std::swap
and then make an unqualified call:
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
What is a friend
function? There is confusion around this area.
Before C++ was standardized, friend
functions did something called "friend name injection", where the code behaved as if if the function had been written in the surrounding namespace. For example, these were equivalent pre-standard:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
However, when ADL was invented this was removed. The friend
function could then only be found via ADL; if you wanted it as a free function, it needed to be declared as so (see this, for example). But lo! There was a problem.
If you just use std::swap(x, y)
, your overload will never be found, because you've explicitly said "look in std
, and nowhere else"! This is why some people suggested writing two functions: one as a function to be found via ADL, and the other to handle explicit std::
qualifications.
But like we saw, this can'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' job to provide std::swap
, it's the swappers' job to make sure they don't use qualified swap
, like above. And this tends to work pretty well, as long as people know about it. But therein lies the problem: it's unintuitive to need to use an unqualified call!
To make this easier, some libraries like Boost provided the function boost::swap
, which just does an unqualified call to swap
, with std::swap
as an associated namespace. This helps make things succinct again, but it's still a bummer.
Note that there is no change in C++11 to the behavior of std::swap
, which I and others mistakenly thought would be the case. If you were bit by this, read here.
In short: the member function is just noise, the specialization is ugly and incomplete, but the friend
function is complete and works. And when you swap, either use boost::swap
or an unqualified swap
with std::swap
associated.
†Informally, a name is associated if it will be considered during a function call. For the details, read §3.4.2. In this case, std::swap
normally isn't considered; but we can associate it (add it to the set of overloads considered by unqualified swap
), allowing it to be found.
这篇关于公共朋友交换成员函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!