std::swap()
используется многими стандартными контейнерами (такими как std::list
и std::vector
) во время сортировки и даже назначения.
Но стандартная реализация swap()
очень обобщена и довольно неэффективна для пользовательских типов.
Таким образом, эффективность может быть повышена за счет перегрузки std::swap()
с помощью реализации конкретного типа. Но как его реализовать, чтобы он использовался контейнерами std?
Ответы:
Правильный способ перегрузки свопа - записать его в том же пространстве имен, что и то, что вы обмениваете, чтобы его можно было найти с помощью поиска, зависящего от аргументов (ADL) . Особенно легко сделать следующее:
источник
std::sort
который использует ADL для замены элементов, не соответствует C ++ 03, но соответствует C ++ 11. Кроме того, почему -1 ответ, основанный на том факте, что клиенты могут использовать неидиоматический код?Внимание Mozza314
Вот симуляция эффектов универсального
std::algorithm
вызоваstd::swap
и предоставления пользователем своей подкачки в пространстве имен std. Поскольку это эксперимент, в этой симуляцииnamespace exp
вместоnamespace std
.Для меня это распечатывает:
Если ваш компилятор выводит что-то другое, значит, он неправильно реализует «двухэтапный поиск» для шаблонов.
Если ваш компилятор соответствует (любому из C ++ 98/03/11), он выдаст тот же результат, что и я. И в этом случае произойдет именно то, чего вы боитесь. И помещение вас
swap
в пространство именstd
(exp
) не остановило этого.Дэйв и я - члены комитета и работаем в этой области стандарта в течение десяти лет (и не всегда в согласии друг с другом). Но этот вопрос решен давно, и мы оба согласны с тем, как он решен. Игнорирование экспертного мнения / ответа Дэйва в этой области на свой страх и риск.
Эта проблема обнаружилась после публикации C ++ 98. Примерно с 2001 года мы с Дэйвом начали работать в этой области . А это современное решение:
Выход:
Обновить
Было сделано наблюдение, что:
работает! Так почему бы не использовать это?
Рассмотрим случай, когда ваш
A
шаблон класса:Теперь опять не работает. :-(
Таким образом, вы можете ввести
swap
пространство имен std и заставить его работать. Но вы должны помнить , чтобы поместитьswap
вA
пространство имен «S для случая , когда у вас есть шаблон:A<T>
. А так как в обоих случаях будет работать , если вы положилиswap
в пространствеA
имен «s, это просто легче запомнить (и учить других) , чтобы просто сделать это , что так.источник
template <>
ваш первый пример, я получил выводexp::swap(A, A)
от gcc. Итак, почему бы не предпочесть специализацию?using std::swap
является исключением из правила «никогда не помещать операторы using в файлы заголовков»? На самом деле, почему бы не положитьusing std::swap
внутрь<algorithm>
? Я полагаю, это могло бы сломать код крошечного меньшинства людей. Может быть, отказаться от поддержки и в конечном итоге добавить ее?using std::swap
объем функций в ваших заголовках. Да,swap
это почти ключевое слово. Но нет, это не совсем ключевое слово. Поэтому лучше не экспортировать его во все пространства имен, пока это действительно не понадобится.swap
очень нравитсяoperator==
. Самая большая разница в том, что никто даже не думает о вызовеoperator==
с использованием квалифицированного синтаксиса пространства имен (это было бы слишком некрасиво).Вам не разрешено (по стандарту C ++) перегружать std :: swap, однако вам специально разрешено добавлять специализации шаблонов для ваших собственных типов в пространство имен std. Например
тогда использование в контейнерах std (и где-либо еще) выберет вашу специализацию вместо общей.
Также обратите внимание, что предоставление реализации подкачки базового класса недостаточно для ваших производных типов. Например, если у вас есть
это будет работать для базовых классов, но если вы попытаетесь поменять местами два производных объекта, он будет использовать общую версию из std, потому что шаблонный обмен является точным совпадением (и это позволяет избежать проблемы замены только `` базовых '' частей ваших производных объектов ).
ПРИМЕЧАНИЕ. Я обновил это, чтобы удалить неправильные фрагменты из моего последнего ответа. D'о! (спасибо puetzk и j_random_hacker за указание на это)
источник
Хотя это правильно, что обычно не следует добавлять что-либо в пространство имен std ::, добавление специализаций шаблонов для определяемых пользователем типов специально разрешено. Перегрузки функций нет. Это небольшая разница :-)
Специализация std :: swap будет выглядеть так:
Без бита шаблона <> это была бы перегрузка, которая не определена, а не разрешенная специализация. Предлагаемый @ Wilka подход к изменению пространства имен по умолчанию может работать с пользовательским кодом (из-за того, что поиск Koenig предпочитает версию без пространства имен), но это не гарантируется и на самом деле не должно (реализация STL должна использовать полностью -квалифицированный std :: swap).
На comp.lang.c ++. Есть ветка, модерируемая с длинным обсуждением этой темы. Однако по большей части речь идет о частичной специализации (что в настоящее время нет хорошего способа сделать).
источник
vector
версии и будет использоваться .::swap
вами перегрузка более специализирована, чемstd::swap
перегрузкаvector
, поэтому она перехватывает вызов, и никакая специализация последнего не имеет значения. Я не уверен, насколько это практическая проблема (но я и не утверждаю, что это хорошая идея!).