C++ 公开好友交换成员功能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5695548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:41:22  来源:igfitidea点击:

public friend swap member function

c++c++11friendcopy-and-swap

提问by towi

In the beautiful answer to the copy-and-swap-idiomthere is a piece of code I need a bit of help:

在对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);
    }
    // ...
};

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.

还有其他主张我们应该为我们的类型专门化 std::swap,提供类内交换以及自由函数交换等。但这都是不必要的:任何正确使用交换都将通过不合格的调用,我们的功能将通过 ADL 找到。一个功能就行。

With friendI am a bit on "unfriendly" terms, I must admit. So, my main questions are:

随着friend我对“不友好”的条款一点,我必须承认。所以,我的主要问题是:

  • looks like a free function, but its inside the class body?
  • why isn't this swapstatic? 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 friendcomes in?
  • 看起来像一个自由函数,但它在类体内?
  • 为什么这不是swap静态的?它显然不使用任何成员变量。
  • “任何正确使用交换都会通过 ADL 找到交换”?ADL 会搜索命名空间,对吧?但它也在类内部查看吗?或者这里是哪里friend进来的?

Side-questions:

附带问题:

  • With C++11, should I mark my swaps with noexcept?
  • With C++11 and its range-for, should I place friend iter begin()and friend iter end()the same way inside the class? I think the friendis not needed here, right?
  • 使用 C++11,我应该用 标记我的swapsnoexcept吗?
  • 随着C ++ 11和它的范围,对,我应该将friend iter begin()friend iter end()在类中以同样的方式?我认为friend这里不需要,对吗?

回答by GManNickG

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 swapfunction.

有几种写法swap,有些比其他的好。然而,随着时间的推移,人们发现单一定义效果最好。让我们考虑如何考虑编写swap函数。



We first see that containers like std::vector<>have a single-argument member function swap, such as:

我们首先看到容器 likestd::vector<>有一个单参数成员函数swap,例如:

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 swapis one of them. Why? Let's go on.

那么,我们班自然也应该这样吧?嗯,不是真的。标准库有各种不必要的东西,成员swap就是其中之一。为什么?我们继续。



What we should do is identify what's canonical, and what our class needsto 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.

我们应该做的是确定什么是规范的,以及我们的类需要做什么来处理它。交换的规范方法是使用std::swap。这就是为什么成员函数没有用:它们不是我们应该如何交换东西,一般来说,并且与std::swap.

Well then, to make std::swapwork we should provide (and std::vector<>should have provided) a specialization of std::swap, right?

那么,为了std::swap工作,我们应该提供(并且std::vector<>应该提供) 的专业化std::swap,对吧?

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: function 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 friendfunction, and find it through ADL:

有!我们可以使用一个friend函数,并通过ADL找到它:

namespace xyz
{
    struct myclass
    {
        friend void swap(myclass&, myclass&);
    };
}

When we want to swap something, we associate?std::swapand then make an unqualified call:

当我们想要交换某些东西时,我们将? 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

What is a friendfunction? There is confusion around this area.

什么是friend函数?该地区存在混乱。

Before C++ was standardized, friendfunctions did something called "friend name injection", where the code behaved as ifif the function had been written in the surrounding namespace. For example, these were equivalent pre-standard:

在 C++ 标准化之前,friend函数做了一些称为“朋友名称注入”的事情,其中​​代码的行为就像函数是在周围的命名空间中编写的一样。例如,这些是等效的预标准:

struct foo
{
    friend void bar()
    {
        // baz
    }
};

// turned into, pre-standard:    

struct foo
{
    friend void bar();
};

void bar()
{
    // baz
}

However, when ADLwas invented this was removed. The friendfunction could then onlybe 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.

然而,当ADL被发明时,它被删除了。friend然后只能通过 ADL 找到该函数;如果您希望将其作为自由函数,则需要将其声明为(例如,请参阅this)。但是!有问题。

If you just use std::swap(x, y), your overload will neverbe 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.

如果你只是使用std::swap(x, y),你的重载将永远不会被发现,因为你已经明确地说“看看std,别无他处”!这就是为什么有些人建议编写两个函数:一个作为通过ADL找到的函数,另一个用于处理显式std::限定。

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!

但正如我们所见,这不能在所有情况下都有效,我们最终会弄得一团糟。相反,惯用的交换走另一条路:不是让类的工作来提供std::swap,而是交换者的工作是确保他们不使用qualified swap,就像上面一样。只要人们知道它,这往往效果很好。但问题就在这里:需要使用不合格的调用是不直观的!

To make this easier, some libraries like Boost provided the function boost::swap, which just does an unqualified call to swap, with std::swapas an associated namespace. This helps make things succinct again, but it's still a bummer.

为了使这更容易,像 Boost 这样的一些库提供了函数boost::swap,它只是对 进行非限定调用swap,并std::swap作为关联的命名空间。这有助于使事情再次简洁,但它仍然令人失望。

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.

请注意,C++11 中 的行为没有变化std::swap,我和其他人错误地认为是这种情况。如果您对此有所了解,请阅读此处



In short: the member function is just noise, the specialization is ugly and incomplete, but the friendfunction is complete and works. And when you swap, either use boost::swapor an unqualified swapwith std::swapassociated.

简而言之:成员函数只是噪音,专业化丑陋且不完整,但friend功能完整且有效。而当你交换时,要么使用boost::swap要么不合格的swapstd::swap关联。



?Informally, a name is associatedif it will be considered during a function call. For the details, read §3.4.2. In this case, std::swapnormally isn't considered; but we can associateit (add it to the set of overloads considered by unqualified swap), allowing it to be found.

? 非正式地,如果将在函数调用期间考虑名称,则该名称是相关联的。有关详细信息,请阅读第 3.4.2 节。在这种情况下,std::swap通常不考虑;但是我们可以将它关联起来(将它添加到 unqualified 考虑的重载集合中swap),从而可以找到它。

回答by Ben Voigt

That code is equivalent (in almostevery way) to:

该代码等效于(几乎在所有方面):

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);
}

A friend function defined inside a class is:

在类中定义的友元函数是:

  • placed in the enclosing namespace
  • automatically inline
  • able to refer to static members of the class without further qualification
  • 放置在封闭的命名空间中
  • 自动地 inline
  • 无需进一步限定即可引用类的静态成员

The exact rules are in section [class.friend](I quote paragraphs 6 and 7 of the C++0x draft):

确切的规则在部分[class.friend](我引用了 C++0x 草案的第 6 段和第 7 段):

A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8), the function name is unqualified, and the function has namespace scope.

Such a function is implicitly inline. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not.

当且仅当该类是非局部类 (9.8)、函数名称未限定且函数具有命名空间作用域时,才能在类的友元声明中定义函数。

这样的函数是隐式内联的。在类中定义的友元函数在定义它的类的(词法)范围内。在类之外定义的友元函数不是。