Почему, когда один член не может быть перемещен, весь закрытый класс не может быть перемещен?

Пример

struct MyObject {
  MyObject(int value):value(value) { }
  MyObject(MyObject const&o):value(o.value) { }

  int value;
};

Предположим, что конструктор копирования делает что-то в дополнение к полезности. Тогда

std::function<void()> f() {
  MyObject o;
  std::vector<int> v;
  return [=]() { /* use v and o */ &o; &v; }
}

v и o сначала копируются в исходный лямбда-объект, что отлично. Но затем они снова копируются каждый раз, когда объект лямбда должен быть перемещен. Хотя v можно перемещать, но это не так. Это потому, что лямбда не имеет неявного конструктора перемещения, потому что o не имеет конструктора перемещения или тривиального конструктора копии.

Может кто-нибудь объяснить объяснение этого?

Ответ 1

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

Среди людей, которые не хотели иметь неявное поколение конструкторов движений, Дэйв Абрахамс написал статью под названием Implicit Move Must Go. Обоснование заключается в том, что при некоторых обстоятельствах, даже если члены подвижны, неявная генерация конструктора перемещения может разбить инварианты.

В начале этого года (2011 г.) комитет решил сохранить неявное генерирование конструкторов перемещения в решении, которое, как представляется, подчеркивало повышение производительности в существующем коде по вопросам безопасности (1) и снова Dave blogged об этом. Он не говорит о специфике решения, плюсах и минусах, но не совсем удовлетворен результатом.

Изменить (от Джерри Коффина): Здесь приведен список условий для неявного объявления конструктора перемещения:

If the definition of a class X does not explicitly declare a move constructor, 
one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.

Основная идея заключается в том, что включение любого из них в класс является признаком того, что неявно сгенерированное перемещение ctor может ошибочно вести себя. Хотя это верно, условия в списке не являются ни необходимыми, ни достаточными для определения, поэтому многие движущиеся координаты, которые были бы полезны, не генерируются, и многие из них могут вызвать проблемы. Хуже того, правила уже давно и достаточно сложны, что их мало кто помнит, и их исправление, вероятно, по крайней мере удвоит их. [конец вклада Джерри /rant ]

(1) Благодаря Джину Бушуеву за понимание того, почему было принято решение

Ответ 2

Подумайте, но я подозреваю, что это может быть связано с исключениями. То есть, конструкторы перемещения должны быть действительно небезопасными, но вызов конструктора перемещения, который может вызвать конструктор копирования, может вызвать его.

(пытаясь обновить мою память от здесь, который, я думаю, рассмотрел эту проблему)

ИЗМЕНИТЬ ДОБАВИТЬ:

И моя догадка была неправильной. Правильный ответ, насколько я могу судить, из здесь. Наличие конструктора копирования является признаком того, что класс имеет инварианты, а созданный по умолчанию конструктор перемещения может не учитывать эти инварианты, и поэтому его не следует генерировать.