2014-11-06 4 views
4

Я, кажется, не понимаю, почему вы бы использовать move assignment operator:Что использовать: перемещение оператора присваивания против оператора присваивания копии

CLASSA & operator=(CLASSA && other); //move assignment operator 

закончилась, copy assignment operator:

CLASSA & operator=(CLASSA other); //copy assignment operator 

move assignment operator принимает только r-value reference, например,

CLASSA a1, a2, a3; 
a1 = a2 + a3; 

В copy assignment operator, other может быть конструктор с использованием copy constructor или move constructor (если other инициализируется с RValue, это может быть двигаться возведенных --Если move-constructor defined--).

Если это copy-constructed, мы будем делать 1 копию, и этой копии нельзя избежать.

Если это move-constructed, то производительность/поведение идентичны характеристикам, производимым при первой перегрузке.

Мои вопросы:

1- Почему один хочет реализовать move assignment operator.

2- Если other построен из r-значения, то который assignment operator компилятор решил бы позвонить? И почему?

+0

1) меньше работы, 2) было бы двусмысленно. –

+0

Итак, вы говорите: для 2) будет вызвана первая перегрузка? – Kam

+0

'CLASSA & operator = (CLASSA && other);' является [оператором присваивания перемещения] (http://en.cppreference.com/w/cpp/language/move_operator). Не знаете, как это изменит то, о чем вы просите. Оператор присваивания копии принимает либо 'CLASSA', либо' const CLASSA & '. – Radiodef

ответ

4

Вы не сравниваете подобное с подобным

Если вы пишете движение только типа как std::unique_ptr то оператор присваивания шаг был бы ваш единственный выбор.

Более типичный случай, когда у вас есть тип для копирования, и в этом случае, я думаю, у вас есть три варианта.

  1. T& operator=(T const&)
  2. T& operator=(T const&) и T& operator=(T&&)
  3. T& operator=(T) и двигаться

Обратите внимание, что наличие как Перегрузки вы предложили в одном классе не вариант, как это будет неоднозначным.

Вариант 1 является традиционным вариантом C++ 98 и в большинстве случаев будет работать нормально. Однако, если вам нужно оптимизировать значения r, вы можете рассмотреть вариант 2 и добавить оператор назначения перемещения.

Заманчиво рассмотреть вариант 3 и передать значение, а затем переместить, что я думаю, это то, что вы предлагаете. В этом случае вам нужно написать только один оператор присваивания. Он принимает l-значения и ценой только одного дополнительного хода принимает значения r, и многие люди будут защищать этот подход.

Однако Херб Саттер указал в своем разговоре "Back to the Basics! Essentials of Modern C++ Style" на CppCon 2014, что этот вариант проблематичен и может быть намного медленнее. В случае l-значений он будет выполнять безусловную копию и не будет повторно использовать существующую емкость. Он предоставляет номера для резервного копирования своих требований. Единственным исключением являются конструкторы, в которых нет существующей емкости для повторного использования, и у вас часто есть много параметров, поэтому пропускная способность может уменьшить количество необходимых перегрузок.

Итак, я предлагаю вам начать с Варианта 1 и перейти к Варианту 2, если вам нужно оптимизировать значения r.

2

Очевидно, что две перегрузки не эквивалентны:

  1. Оператор присваивания принимает ссылку RValue работает только с rvalues ​​находятся на правой части выражения. Для поддержки lvalues ​​потребуется другая перегрузка, например, с использованием T const& для типов с возможностью копирования. Конечно, для типов только для перемещения, таких как std::unique_ptr<T>, определение этого оператора присваивания является подходящим выбором.
  2. Оператор присваивания, принимающий значение, охватывает как присвоения rvalue, так и lvalue, предполагая, что этот тип является как конструктивным, так и скользящим. Его каноническая реализация заключается в вызове swap(), чтобы заменить состояние объекта состоянием с правой стороны. Преимущество состоит в том, что копирование/перемещение конструкции аргумента часто может быть устранено.

В любом случае, вы бы не захотели иметь обе перегрузки в одном классе! При назначении из lvalue, очевидно, будет выбрана версия, принимающая значение (другой вариант не является жизнеспособным). Однако оба оператора присваивания являются жизнеспособными при назначении значения r, т. Е. Будет двусмысленность. Это можно легко проверить, пытаясь скомпилировать этот код:

struct foo 
{ 
    void operator=(foo&&) {} 
    void operator=(foo) {} 
}; 

int main() 
{ 
    foo f; 
    f = foo(); 
} 

Чтобы справиться с MOVE- и скопировать конструкцию отдельно можно определить пару операторов присваивания с использованием T&& и T const& в качестве аргументов. Тем не менее, это приводит к необходимости реализовать две версии по существу одного и того же назначения копий, имея только T, поскольку для аргумента требуется только одно назначение копирования.

Таким образом, есть два очевидных варианта:

  1. Для переезда только тип вы бы определить T::operator= (T&&).
  2. Для скопированного типа вы бы определили T::operator=(T).
+1

[Herb sutter указывает] (https://www.youtube.com/watch?v=xnqTKD8uD64&t=1h03m44s), что использование побочной стоимости через этот способ может быть намного медленнее, чем передавать по ссылке-на-const из-за создания безоговорочную копию и повторное использование существующей емкости. –

Смежные вопросы