Monday, August 07, 2006
std::swap и ADL
В книге уважаемого мною автора Скота Мейерса "Эффективное использование С++" в правиле 25 даются рекомендация по использованию std::swap.
Это очень правильные рекомендации, им стоит придерживаться. Вот только жизнь часто вносит свои коррективы в теорию.
Мейерс рассуждает о возможных действиях, когда необходимо как бы частично специализировать std::swap. "Как бы" из-за того, что частичная специализация функций в C++ и перегрузка функций в std:: запрещена стандартом.
Что же делать, рассуждает он, если по-прежнему нужен способ, чтобы разрешить другим людям вызывать swap и иметь более эффективную шаблонную версию?
В результате он принимает решение об объявлении функции swap в той же области видимости что и тип, являющийся ее аргументом. Вызов swap же не квалифицировать и поместить перед ним объявление using std::swap. Когда компилятор встречает вызов swap, он ищет, какую версию вызвать.
Следуя правилам разрешения имен в С++ (используется ADL) будет найдена шаблонная функция foo::swap. При ее отсутствии будет вызвана шаблонная функция std::swap.
Стандарт 3.4.2 стих 2а.
Итак, казалось бы жизнь прекрасна, проблема решена. Но не тут то было. Чудовищные нюансы поджидают нас в компиляторе gcc. Стоит лишь поместить функции test в ту же область видимости что и swap (в примере foo) и код сгенерированный gcc вызовет std::swap вместо foo::swap.
Явная бага компилятора. Разработчики gcc же отвечают крайне невнятно. В последней версии gcc (на данный момент это 4.1.0) проблема по прежнему осталась.
Так что острожнее.
Beware of bugs.
Это очень правильные рекомендации, им стоит придерживаться. Вот только жизнь часто вносит свои коррективы в теорию.
Мейерс рассуждает о возможных действиях, когда необходимо как бы частично специализировать std::swap. "Как бы" из-за того, что частичная специализация функций в C++ и перегрузка функций в std:: запрещена стандартом.
Что же делать, рассуждает он, если по-прежнему нужен способ, чтобы разрешить другим людям вызывать swap и иметь более эффективную шаблонную версию?
В результате он принимает решение об объявлении функции swap в той же области видимости что и тип, являющийся ее аргументом. Вызов swap же не квалифицировать и поместить перед ним объявление using std::swap. Когда компилятор встречает вызов swap, он ищет, какую версию вызвать.
namespace foo
{
template <class T> class bar {};
template <class T> void swap(bar<T>&, bar<T>&) {}
}
void test()
{
foo::bar<void> a, b;
using std::swap;
swap(a, b);
}
Следуя правилам разрешения имен в С++ (используется ADL) будет найдена шаблонная функция foo::swap. При ее отсутствии будет вызвана шаблонная функция std::swap.
Стандарт 3.4.2 стих 2а.
Итак, казалось бы жизнь прекрасна, проблема решена. Но не тут то было. Чудовищные нюансы поджидают нас в компиляторе gcc. Стоит лишь поместить функции test в ту же область видимости что и swap (в примере foo) и код сгенерированный gcc вызовет std::swap вместо foo::swap.
Явная бага компилятора. Разработчики gcc же отвечают крайне невнятно. В последней версии gcc (на данный момент это 4.1.0) проблема по прежнему осталась.
Так что острожнее.
Beware of bugs.
