公众朋友交换成员函数

在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不是静态的 ? 它显然不使用任何成员变量。
  • “任何适当的交换使用将通过ADL找出交换” ? ADL将搜索命名空间,对吧? 但它是否也在内部看类? 或者在这里friend进来?
  • 副题:

  • 用C ++ 11,我应该用noexcept标记我的swap吗?
  • 对于C ++ 11及其范围 ,我应该在类内部以同样的方式将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

    上一篇: public friend swap member function

    下一篇: Dynamically allocating an array of objects