公共朋友交换成员函数 [英] public friend swap member function

查看:205
本文介绍了公共朋友交换成员函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在对复制和交换惯例的美丽答案中有一段代码我需要一点帮助:

  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 是其中之一。为什么?让我们继续。






    我们应该做的是识别什么是标准,以及我们的类 >做与它合作。而交换的规范方法是使用 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?



    在C ++标准化之前, 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 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 friend 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆