Страницы

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

воскресенье, 15 декабря 2019 г.

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

#cpp #cpp11 #оператор_присваивания


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

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


Ответы

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

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

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