А был ли мальчик? (помянем плюсы)

User avatar
AndreyT
Уже с Приветом
Posts: 3003
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Мальчик-Одуванчик wrote: 27 Jan 2018 02:22 Вот в этом примере с помощью свопа реализовано одновременно и копирующее и перемещающее присваивание.
Ну, во-первых, формальная семантика перемещения является расслабленным вариантом семантики копирования, т.е. с формальной точки зрения любое "классическое" копирование одновременно является перемещением. Поэтому ваше утверждение "реализовано одновременно и копирующее и перемещающее присваивание" является в этом смысле тавтологией.

Это отражено и в дизайне языка: тип `T &&` приводим к типу `const T &`, то если если в классе не объявлена перемещающая функция (напр. конструктор или оператор присваивания с параметром `T &&`), то тут же на помощь придет и будет вызвана аналогичная копирующая функция (напр. конструктор или оператор присваивания с параметром `const T &`). В этом нет никакого нарушения семантики перемещения.

Всякий раз, когда вы реализуете полноценное копирование для некоторого класса, вы тем самым "покрываете" и семантику перемещения для этого класса. Покрываете неэффективно, но формально корректно.

Однако, как вы сами понимаете, семантика перемещения была введена в язык не для этого, чтобы лениво использовать копирование там, где можно сделать перемещение и "радоваться, что все работает". Более расслабленные требования семантики перемещения позволяют вам реализовывать перемещение существенно более эффективно. То есть вас никто не заставляет реализовывать эффективное перемещение, вам лишь предоставляется такая возможность. Для копируемых типов семантика перемещения - это лишь опциональная optimization opportunity. А уж найти места, где более эффективная семантика перемещения заслуживает аккуратной отдельной реализации - ваша задача.
Мальчик-Одуванчик wrote: 27 Jan 2018 02:22Разумеется при наличии обеих форм конструктора копирования.
Class A {
A& operator=(A source) {
swap(*this, source);
return *this;
}
....
}
Это верно. В этом и заключается смысл уже упоминавшегося мною выше совета - если вы знаете, что внутри функции вам понадобится копия аргумента, то лучше дать компилятору сделать за вас эту копию (предав аргумент по значению), а не выполнять копирование самостоятельно внутри функции. Таким способом вы сможете одной-единственной реализацией корректно покрыть и семантику копирования, и семантику перемещения. В данном случае реализация получается весьма и весьма эффективной (отдельные реализации для копирования и для перемещения можно сделать эффективнее "на пару тактов", но в большинстве случаев это не нужно.)

(Однако эта реализация не реализует swap semantics, т.е. не занимается сохранением ресурсов левой части в правой, о котором я говорил выше. Swap здесь служит совсем другой цели.)
Best regards,
Андрей

Return to “Вопросы и новости IT”