Итак имеется два оператора присваивания - копирующий и перемещающий, например такие:
Implementation& operator= (const Implementation & other) noexcept;
Implementation& operator= ( Implementation && other) noexcept;
При этом в данном вопросе (в первом ответе) говорится, что есть определенные преимущества объявления оператора = следующим образом, т.е. по значению:
Implementation& operator= ( Implementation other) noexcept;
Т.е. в таком случае мы можем не объявлять оператор, принимающий &&? Иначе просто возникнет неопределенность. Почему же тогда в стандартной библиотеке используется первая техника, а в бусте можно найти такое:
#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
template < typename ValueType >
any& operator= (const ValueType& rhs)
{
any(rhs).swap(*this);
return *this;
}
any& operator= (any rhs)
{
any(rhs).swap(*this);
return *this;
}
#else
any& operator= (const any& rhs)
{
any(rhs).swap(*this);
return *this;
}
any& operator= (any&& rhs) BOOST_NOEXCEPT
{
rhs.swap(*this);
any().swap(rhs);
return *this;
}
#endif
Почему не сделать во втором блоке препроцессора ту же реализацию:
any& operator= (any rhs)
{
any(rhs).swap(*this);
return *this;
}
Или это сделано для того, чтобы в операторе =, принимающем && сделать обнуление rvalue значения?
Ответ
Почему не сделать во втором блоке препроцессора ту же реализацию:
any& operator= (any rhs)
{
any(rhs).swap(*this);
return *this;
}
Дело в том, что с приходом C++11 и появлением семантики перемещения появилась возможность разделить ситуации, когда нам действительно нужна новая копия объекта и ситуации, когда мы хотим только переместить один объект в другой. При таком разделении копирующий оператор присваивания (или конструктор) может бросать исключения, а перемещающий оператор практически всегда можно реализовать без бросания исключений и без необходимости создавать временные объекты. Соответственно с переходом на С++11 приведенный конструктор получится неоптимальным так как 1) он может кидать исключения 2) он всегда создает временный объект.
Пример сценария с оверхедом:
any first{};
any second{};
first = ::std::move(second); // создаем еще и третий объект - аргумент оператора
Использовать copy-and-swap оператор присваивания совместно с перемещающим оператором присваивания не получится, так как в этом случае при присваивании rvalue reference компилятор не сможет выбрать между ними.
Комментариев нет:
Отправить комментарий