Почему здесь задействован конструктор перемещения - PullRequest
0 голосов
/ 27 июня 2018

У меня есть этот кусок кода C ++:

class Args {};

class MyClass {
  public:
  MyClass(Args& a) {}
  MyClass(MyClass &&) = delete;
};

int main() {

  Args a;
  MyClass c1 = MyClass(a);
  MyClass c2 = a;
  MyClass c3(a);

  return 0;
}

Это не компилируется, потому что при создании объектов c1 и c2, по-видимому, используется конструктор перемещения класса:

error: use of deleted function ‘MyClass::MyClass(MyClass&&)’

Создается впечатление, что компилятор хочет создать временный объект и затем переместить его в c1 и c2. Почему это происходит? Разве все три оператора не должны просто вызывать конструктор MyClass(Args& a)?

С другой стороны, если я создаю конструктор перемещения, программа прекрасно компилируется, и конструктор перемещения никогда не вызывается !!!

Ответы [ 3 ]

0 голосов
/ 27 июня 2018

Почему это происходит? Разве все три оператора не должны просто вызывать конструктор MyClass(Args& a)?

Для MyClass c1 = MyClass(a); и MyClass c2 = a; временный MyClass будет сначала создан конструктором MyClass::MyClass(Args& a), а затем использован для инициализации копирования c1 и c2. Созданные временные значения - это rvalues, это означает, что для инициализации копии будет выбран move-constructor.

С другой стороны, если я создаю конструктор перемещения, программа прекрасно компилируется, и конструктор перемещения никогда не вызывается !!!

Причина: copy elision ; операция копирования / перемещения здесь опущена, в результате MyClass::MyClass(Args& a) используется для непосредственной инициализации объекта c1 и c2.

Правило об исключении копирования изменилось с C ++ 17. Обратите внимание, что до C ++ 17 исключение копирования не гарантируется. А для негарантированного удаления копии, даже если операция копирования / перемещения не указана, конструктор копирования / перемещения все еще должен присутствовать и быть доступным.

Это оптимизация: даже когда это происходит, и конструктор копирования / перемещения не вызывается, он все равно должен присутствовать и быть доступным (как если бы оптимизация вообще не происходила), в противном случае программа плохо сформирована.

После C ++ 17 ваш код будет работать нормально. Для гарантированного удаления копии конструктор копирования / перемещения не обязательно должен присутствовать или быть доступным.

При следующих обстоятельствах компиляторы обязаны опускать копирование и перемещение объектов класса, даже если Конструктор копирования / перемещения и деструктор имеют видимые побочные эффекты. Они не должны присутствовать или быть доступными, так как языковые правила что операция копирования / перемещения не выполняется, даже концептуально:

  • При инициализации, если выражение инициализатора является prvalue и cv-неквалифицированная версия исходного типа является тем же классом, что и класс места назначения, выражение инициализатора используется для инициализировать целевой объект:

    T x = T(T(T())); // only one call to default constructor of T, to initialize x
    
0 голосов
/ 27 июня 2018

Одна из основных проблем заключается в следующем:

MyClass c1 = MyClass(a);

Это создает временный объект типа MyClass, а временные типы являются значениями. Затем он пытается либо copy-construct c1 с использованием временного объекта, либо, если у вас есть move-constructor, тогда move-construct c1.

В C ++ 11 и C ++ 14 не будет автоматически сгенерированного конструктора копирования для вашего класса (потому что у вас есть определяемый пользователем (даже если удален) конструктор перемещения), поэтому только удаленный ход -строитель доступен. Ну, он будет доступен, если не будет удален, что приведет к вашей ошибке.

0 голосов
/ 27 июня 2018

См. копия elision :

При следующих обстоятельствах компиляторам разрешено, но не обязательно опускать конструкцию объектов копирования и перемещения (начиная с C ++ 11), даже если конструктор копирования / перемещения (начиная с C ++ 11) и У деструктора наблюдаемые побочные эффекты. Это оптимизация: , даже если это происходит, и конструктор копирования / перемещения не вызывается, он все равно должен присутствовать и быть доступным (как если бы оптимизация вообще не происходила), в противном случае программа некорректна.

Начиная с C ++ 17:

Они не обязательно должны присутствовать или быть доступными, так как языковые правила гарантируют, что операция копирования / перемещения не выполняется, даже концептуально.

...