Страницы

Поиск по вопросам

пятница, 26 октября 2018 г.

2 оператора присваивания или один?

Итак имеется два оператора присваивания - копирующий и перемещающий, например такие:
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 компилятор не сможет выбрать между ними.

Комментариев нет:

Отправить комментарий