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

мог спать прошлой ночью и начал думать оstd::swap, Вот знакомая версия C ++ 98:

template <typename T>
void swap(T& a, T& b)
{
    T c(a);
    a = b;
    b = c;
}

Если пользовательский классFoo использует внешние ресурсы, это неэффективно. Общая идиома - предоставить методvoid Foo::swap(Foo& other) и специализацияstd::swap<Foo>, Обратите внимание, что это не работает с классомшаблоны так как вы не можете частично специализировать шаблон функции и перегружать имена вstd Пространство имен незаконно. Решение состоит в том, чтобы написать шаблонную функцию в собственном пространстве имен и полагаться на поиск в зависимости от аргумента, чтобы найти его. Это критически зависит от клиента, чтобы следоватьusing std::swap идиома "вместо вызоваstd::swap непосредственно. Очень хрупкий

В C ++ 0x, еслиFoo имеет пользовательский конструктор перемещения и оператор присваивания, предоставляяswap метод иstd::swap<Foo> специализация практически не дает выигрыша в производительности, потому что версия C ++ 0xstd::swap использует эффективные ходы вместо копий:

#include <utility>

template <typename T>
void swap(T& a, T& b)
{
    T c(std::move(a));
    a = std::move(b);
    b = std::move(c);
}

Не нужно возиться сswap больше уже отнимает много бремени у программиста. Текущие компиляторы еще не генерируют конструкторы перемещения и операторы присваивания перемещения автоматически, но, насколько я знаю, это изменится. Единственная оставшаяся проблема - безопасность исключений, потому что в общем случае операции перемещения разрешены, и это открывает целую банку червей. Вопрос "Каково состояние перемещенного объекта?" усложняет вещи дальше.

Тогда я думал, что именно семантикаstd::swap в C ++ 0x все ли хорошо? Каково состояние объектов до и после обмена? Как правило, обмен с помощью операций перемещения не затрагивает внешние ресурсы, а только сами «плоские» представления объектов.

Так почему бы просто не написатьswap шаблон, который делает именно это:поменять местами представления объектов?

#include <cstring>

template <typename T>
void swap(T& a, T& b)
{
    unsigned char c[sizeof(T)];

    memcpy( c, &a, sizeof(T));
    memcpy(&a, &b, sizeof(T));
    memcpy(&b,  c, sizeof(T));
}

Это настолько эффективно, насколько это возможно: оно просто взрывается через сырую память. Это не требует какого-либо вмешательства со стороны пользователя: не нужно определять никаких специальных методов обмена или операций перемещения. Это означает, что он работает даже в C ++ 98 (который не имеет ссылок на значения), заметьте). Но что еще более важно,теперь мы можем забыть о проблемах безопасности исключений, потому чтоmemcpy никогда не бросает.

Я вижу две потенциальные проблемы с этим подходом:

Во-первых, не все объекты предназначены для обмена. Если конструктор класса скрывает конструктор копирования или оператор присваивания копии, попытка поменять объекты класса не удастся во время компиляции. Мы можем просто ввести некоторый мертвый код, который проверяет, допустимо ли копирование и присваивание для типа:

template <typename T>
void swap(T& a, T& b)
{
    if (false)    // dead code, never executed
    {
        T c(a);   // copy-constructible?
        a = b;    // assignable?
    }

    unsigned char c[sizeof(T)];

    std::memcpy( c, &a, sizeof(T));
    std::memcpy(&a, &b, sizeof(T));
    std::memcpy(&b,  c, sizeof(T));
}

Любой достойный компилятор может тривиально избавиться от мертвого кода. (Возможно, есть лучшие способы проверить «соответствие свопа», но это не главное. Важно то, что это возможно).

Во-вторых, некоторые типы могут выполнять «необычные» действия в конструкторе копирования и операторе присваивания копии. Например, они могут уведомить наблюдателей об их изменении. Я считаю это незначительной проблемой, потому что такие объекты, вероятно, не должны были обеспечивать операции копирования в первую очередь.

Пожалуйста, дайте мне знать, что вы думаете об этом подходе к обмену. Будет ли это работать на практике? Вы бы использовали это? Можете ли вы определить типы библиотек, где это может сломаться? Видите ли вы дополнительные проблемы? Обсудить!

Ответы на вопрос(5)

Ваш ответ на вопрос