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

200
19 ноября 2017, 16:19

Итак имеется два оператора присваивания - копирующий и перемещающий, например такие:

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 значения?

Answer 1

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

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

READ ALSO
Какие функции swap нужны для класса

Какие функции swap нужны для класса

В данный момент имеется функция-член класса swap:

239
Расширение строки QListView

Расширение строки QListView

У меня есть QListView и модель для него, унаследованная от QAbstractListModelМне необходимо отображать элементы модели при помощи кастомных виджетов

270
Как получить из лямбды значение, которое она вернёт?

Как получить из лямбды значение, которое она вернёт?

Я бы хотел таким образом получить максимальный элемент field, но не понимаю, как получить возвращаемое значение

257
Pulseview build windows msys2

Pulseview build windows msys2

Пытаюсь собрать sigrok pulseview использую msys2Все время получаю ошибки или не найти хедеры или проблемы с библиотеками у линковщика, последнее что...

244