Я предлагаю эту реализацию swap
, если она действительна, превосходит текущую реализацию std::swap
:
#include <new>
#include <type_traits>
template<typename T>
auto swap(T &t1, T &t2) ->
typename std::enable_if<
std::is_nothrow_move_constructible<T>::value
>::type
{
alignas(T) char space[sizeof(T)];
auto asT = new(space) T{std::move(t1)};
// a bunch of chars are allowed to alias T
new(&t1) T{std::move(t2)};
new(&t2) T{std::move(*asT)};
}
Страница для std::swap
в cppreference подразумевает, что она использует перемещение-назначение, поскольку спецификация noexcept
зависит от того, является ли перемещение-перемещение без-броска. Кроме того, здесь был задан вопрос , как swap
реализован , и это то, что я вижу в реализациях для libstdc ++ и libc ++
template<typename T>
void typicalImplementation(T &t1, T &t2)
noexcept(
std::is_nothrow_move_constructible<T>::value &&
std::is_nothrow_move_assignable<T>::value
)
{
T tmp{std::move(t1)};
t1 = std::move(t2);
// this move-assignment may do work on t2 which is
// unnecessary since t2 will be reused immediately
t2 = std::move(tmp);
// this move-assignment may do work on tmp which is
// unnecessary since tmp will be immediately destroyed
// implicitly tmp is destroyed
}
Я не люблю использовать перемещение-присваивание, как в t1 = std::move(t2)
, потому что это подразумевает выполнение кода для освобождения ресурсов, хранящихся в t1
, если ресурсы удерживаются, даже если известно, что ресурсы в t1
уже освобождены. У меня есть практический случай, когда освобождение ресурсов происходит через вызов виртуального метода, таким образом, компилятор не может устранить эту ненужную работу, потому что он не может знать виртуальный код переопределения, каким бы он ни был, ничего не будет делать, потому что нет ресурсы для релиза в t1
.
Если на практике это незаконно, не могли бы вы указать, что оно нарушает в стандарте?
До сих пор я видел в ответах и комментариях два возражения, которые могут сделать это незаконным:
- Эфемерный объект, созданный в
tmp
, не разрушается, но в пользовательском коде может быть некоторое предположение, что, если T
создан, он будет разрушен
T
может быть типом с константами или ссылками, которые нельзя изменить, может быть реализовано назначение перемещения, меняя ресурсы, не касаясь этих констант или связывая ссылки.
Таким образом, кажется, что эта конструкция является допустимой для любого типа, кроме тех, которые встречаются в случае 1 или 2 выше.
Для иллюстрации я поместил ссылку на страницу проводника компилятора *1041*, которая показывает все три реализации для случая замены векторов целых чисел, то есть типичной реализации по умолчанию std::swap
, специализированной для vector
, и тот, который я предлагаю. Вы можете увидеть, что предлагаемая реализация выполняет меньше работы, чем обычная, точно так же, как специализированная в стандарте.
Только пользователь может решить поменяться, выполняя "все-ход-конструирование", вместо "одного-строительного хода, двух назначений на ход", ваши ответы информируют пользователей о том, что "все-ход-строительство" недействительна.
После дополнительных побочных разговоров с коллегами, то, что я прошу, сводится к тому, что это работает для типов, в которых движение можно рассматривать как разрушительное, поэтому нет необходимости балансировать конструкции с разрушениями.