Изменит ли C++11 поведение явного вызова std::swap для обеспечения поиска ADL-локализованных свопов, подобно boost::swap?

Предыстория

Рассмотрим для этого вопроса следующий код:

#include 

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 
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 
void do_swap(T& lhs, T& rhs)
{
    std::swap(lhs, rhs);
}

Явно указывая std::, она запрещает ns::swap быть найденной через аргументно-зависимый поиск. (Затем он не компилируется, потому что std::swap пытается скопировать foo, что недопустимо). Вместо этого мы делаем так:

template 
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::swap найден, а std::swap, будучи менее специализированным, не используется. Это более уродливо, но работает и понятно задним числом. boost::swap хорошо обворачивает это для нас (и обеспечивает перегрузку массивов):

#include 

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

Вопрос

Мой вопрос таков: принимает ли std::swap поведение boost::swap в C++11? Если нет, то почему?

Мне кажется очевидным, что так и должно быть. Любой код, нарушенный изменением, вероятно, изначально был довольно хлипким (алгоритмы и контейнеры, такие как std::sort и std::vector, были неспецифицированы; реализациям разрешалось вызывать ADL swap'ы или нет неопределенно), так что изменение будет к лучшему. Кроме того, std::swap теперь определен для массивов, так что изменение вообще не исключено.

Однако, хотя в §17.6.3.2 указано, что все вызовы swap в стандартной библиотеке должны выполняться без std: квалификации (исправляя проблему с алгоритмами и контейнерами, отмеченную выше), он не затрагивает std::swap как таковой. В нем даже приводятся примеры замены значений, включающие using std::swap;. Аналогично, в §20.2.2 (где указан std::swap) нет ни слова об ADL.

Наконец, GCC не включает ADL в свою реализацию std::swap (как и MSVC, но это мало о чем говорит). Так что я, должно быть, ошибаюсь, что std::swap принимает поведение boost::swap, но я не понимаю, почему изменение не было сделано :( И я не одинок!

29
задан Community 23 May 2017 в 12:13
поделиться

1 ответ

Вот реализация концепции:

#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);
}
3
ответ дан 28 November 2019 в 01:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: