Рассмотрим для этого вопроса следующий код:
#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
, но я не понимаю, почему изменение не было сделано :( И я не одинок!
Вот реализация концепции:
#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);
}