Подтвердить что ты не робот

Как стандартная библиотека реализует std:: swap?

Как функция swap реализована в STL? Это так просто:

template<typename T> void swap(T& t1, T& t2) {
    T tmp(t1);
    t1=t2;
    t2=tmp;
}

В других сообщениях они говорят об специализировании этой функции для вашего собственного класса. Зачем мне это нужно? Почему я не могу использовать функцию std::swap?

4b9b3361

Ответ 1

Как реализовано std::swap?

Да, реализация, представленная в вопросе, представляет собой классический С++ 03.

Более современная (С++ 11) реализация std::swap выглядит следующим образом:

template<typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1); // or T temp(std::move(t1));
    t1 = std::move(t2);
    t2 = std::move(temp);
}

Это улучшение по сравнению с классической реализацией С++ 03 с точки зрения управления ресурсами, поскольку оно предотвращает ненужные копии и т.д. Это, С++ 11 std::swap, для типа T должен быть MoveConstructible и MoveAssignable, что позволяет реализовать и улучшить.

Зачем мне нужно создавать пользовательскую реализацию?

Индивидуальная реализация swap для определенного типа обычно рекомендуется, когда ваша реализация более эффективна или специфична, чем стандартная версия.

Классическим примером этого является то, что ваш класс управляет большим количеством ресурсов, которые будут дорого копировать, а затем удалять. Вместо этого ваша пользовательская реализация может просто обмениваться ручками или указателями, необходимыми для выполнения обмена.

С появлением std::move и подвижных типов (и реализовал ваш тип как таковой), примерно С++ 11 и далее, многие оригинальные рассуждения здесь начинают отпадать; но тем не менее, если пользовательский своп будет лучше стандартного, реализуйте его.

Общий код обычно может использовать ваш пользовательский swap, если он соответствующим образом использует механизм ADL.

Ответ 2

Как функция swap реализована в STL?

Какая реализация? Это спецификация, а не отдельная конкретная библиотека. Если вы имеете в виду, как это делает моя стандартная библиотека компилятора, сообщите нам, какой именно компилятор, или читайте код самостоятельно.

Это так просто:

Это по сути наивная версия pre-С++ 11.

Эта неспециализированная реализация заставляет копию: для T = std::vector<SomethingExpensive> в вашем примере код переводится как:

template<typename T> void swap(T& t1, T& t2) {
  T tmp(t1); // duplicate t1, making an expensive copy of each element
  t1=t2;     // discard the original contents of t1,
             // and replace them with an expensive duplicate of t2
  t2=tmp;    // discard the original contents of t2,
             // and replace them with an expensive duplicate of tmp
}            // implicitly destroy the expensive temporary copy of t1

поэтому для обмена двумя векторами мы по существу создали три. Было три динамических распределения и много дорогостоящих объектов, и любая из этих операций могла быть выбрана, возможно, оставив аргументы в неопределенном состоянии.

Поскольку это было явно ужасно, для дорогостоящих контейнеров были предусмотрены перегрузки, и вам было предложено написать перегрузки для ваших собственных дорогих типов: например. специализация std::vector имела доступ к внутренним векторам и могла менять два вектора без копирования:

template <typename T> void swap(vector<T> &v1, vector<T> &v2) { v1.swap(v2); }
template <typename T> void vector<T>::swap(vector<T>& other) {
  swap(this->size_, other.size_); // cheap integer swap of allocated count
  swap(this->used_, other.used_); // cheap integer swap of used count
  swap(this->data__, other.data_); // cheap pointer swap of data ptr
}

Обратите внимание, что это не требует никаких копий вообще ничего дорогого, никакого динамического (де) распределения и гарантировано не бросать.

Теперь причиной этой специализации является то, что vector:: swap имеет доступ к внутренним векторам и может безопасно и эффективно перемещать их без копирования.

Зачем мне это нужно [специализируется... для вашего собственного класса]?

Pre-С++ 11 по той же причине, что и std::vector, чтобы сделать обмен безопасным и безопасным.

Так как С++ 11, вы действительно этого не делаете - если вы либо предоставляете конструкцию перемещения и назначение, либо компилятор может генерировать нормальные значения по умолчанию для вас.

Новый общий своп:

template <typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1);
    t1 = std::move(t2);
    t2 = std::move(temp);
}

может использовать конструкцию перемещения/назначение, чтобы получить по существу то же поведение, что и реализация пользовательского вектора выше, без необходимости писать собственную реализацию вообще.