C ++ 11是否会更改显式调用std :: swap的行为以确保ADL

背景

考虑这个问题下面的代码:

#include <utility>

namespace ns
{
    struct foo
    {
        foo() : i(0) {}
        int i;

    private:
        foo(const foo&); // not defined,
        foo& operator=(const foo&); // non-copyable
    };

    void swap(foo& lhs, foo& rhs)
    {
        std::swap(lhs.i, rhs.i);
    }
}

template <typename T>
void do_swap(T& lhs, T& rhs); // implementation to be determined

int main()
{
    ns::foo a, b;
    do_swap(a, b);
}

在C ++ 03中,这个do_swap实现将被视为“破坏”:

template <typename T>
void do_swap(T& lhs, T& rhs)
{
    std::swap(lhs, rhs);
}

通过明确指定std:: ,它禁止通过参数相关查找找到ns::swap 。 (然后它无法编译,因为std::swap试图复制foo ,这是不允许的。)相反,我们这样做:

template <typename T>
void do_swap(T& lhs, T& rhs)
{
    using std::swap; // allow std::swap as a backup if ADL fails to find a swap
    swap(lhs, rhs); // unqualified call to swap, allow ADL to operate
}

现在找到了ns::swapstd::swap没有被使用。 这是丑陋的,但它的工作原理,后视可以理解。 boost::swap为我们提供了很好的包装(并提供了数组重载):

#include <boost/swap.hpp>

template <typename T>
void do_swap(T& lhs, T& rhs)
{
    boost::swap(lhs, rhs); // internally does what do_swap did above
}

因此,我的问题是std::swap boost::swap在C ++ 11中采用boost::swap的行为吗? 如果不是,为什么?

对我来说,它应该是显而易见的。 任何由变更破坏的代码首先可能都很脆弱(算法和容器,比如std::sortstd::vector ,被低估;实现被允许调用ADL swap或者不确定),所以变化是为了更好。 另外,现在为数组定义了std::swap ,所以当然不会改变。

但是,虽然第17.6.3.2节规定所有在标准库中swap调用都必须在没有std:: qualification的情况下完成(解决了上述算法和容器的问题),但它并未触及std::swap本身。 它甚至给出了交换值的例子,包括using std::swap; 。 同样§20.2.2(其中指定了std::swap )在ADL上不会说一个字。

最后,GCC不在他们的std::swap实现中启用ADL(MSVC也没有,但是这并不多说)。 所以我必须错误的是, std::swap引发了boost::swap的行为,但我不明白为什么没有做出更改。 :(我并不孤单!


如果有人提议,我将不得不投票反对你的概念验证实施。 我担心它会打破下面的代码,我敢肯定,在过去的十几年里,我至少在野外见过一次或两次。

namespace oops
{

    struct foo
    {
        foo() : i(0) {}
        int i;

        void swap(foo& x) {std::swap(*this, x);}
    };

    void swap(foo& lhs, foo& rhs)
    {
        lhs.swap(rhs);
    }

}

无论您认为上述代码是好代码还是坏代码,它都可以像C ++ 98/03中的作者一样使用,因此用于默默打破它的栏非常高。 告诉用户在C ++ 11中,他们不再需要using std::swap;来编写代码using std::swap; 并没有足够的好处来弥补将上面的代码无声地转换成无限递归的缺点。

using std::swap;取消写入的另一种方法using std::swap; 是使用std::iter_swap来代替:

template <typename T>
void do_swap(T& lhs, T& rhs)
{
    std::iter_swap(&lhs, &rhs); // internally does what do_swap did above
}

这是一个概念验证实现:

#include <utility>

// exposition implementation
namespace std_
{
    namespace detail
    {
        // actual fallback implementation
        template <typename T>
        void swap(T& lhs, T& rhs)
        {
            T temp = std::move(lhs);
            lhs = std::move(rhs);
            rhs = std::move(temp);
        }
    }

    template <typename T>
    void swap(T& lhs, T& rhs)
    {
        using detail::swap; // shadows std_::swap, stops recursion
        swap(lhs, rhs); // unqualified call, allows ADL
    }
}

namespace ns
{
    struct foo
    {
        foo() : i(0) {}
        int i;

    private:
        foo(const foo&); // not defined,
        foo& operator=(const foo&); // non-copyable
    };

    void swap(foo& lhs, foo& rhs)
    {
        std::swap(lhs.i, rhs.i);
    }
}


int main()
{
    int i = 0, j = 0;
    std_::swap(i, j);

    ns::foo a, b;
    std_::swap(a, b);
}

那么, boost::swap()调度到std::swap() 。 为了让std::swap()做类似于boost::swap()事情,它需要委托别的地方。 这是什么在别的地方? 该标准没有强制提供实际实现的另一个版本的swap() 。 这可以做,但标准没有强制要求。

为什么它不这样做? 我没有看到任何提案提出这个实施。 如果有人想要这样做,我相信它会提出。

链接地址: http://www.djcxy.com/p/5051.html

上一篇: Does C++11 change the behavior of explicitly calling std::swap to ensure ADL

下一篇: Confusion about pointers and references in C++