Почему все итераторы/итераторные адаптеры не подвижны в С++ 11?

В этом обсуждался вопрос Когда нужно сделать тип, не подлежащий перемещению в С++ 11, и я обнаружил, что у Скотта Мейерса был аналогичный вопрос о comp.std.С++, где SG, перечисленные ниже, не могут быть перемещены в С++ 11 libeary.

  • все типы mutex (recursive_mutex, timed_mutex, recursive_timed_mutex,
  • condition_variable
  • type_info
  • error_category
  • locale:: facet
  • random_device
  • seed_seq
  • reference_wrapper
  • Продолжительность
  • time_point
  • - все итераторы/итераторные адаптеры
  • ios_base
  • basic_istream:: сторожевая
  • basic_ostream:: сторожевая
  • все атомные типы
  • once_flag

Вопрос в том, почему all iterators / iterator adaptors не движется?

Ответ 1

Эта должность, за год до ратификации стандарта, устарела. Плакат Дэниэл Крюглер, активный член комитета, и это немного политическое лоббирование:

Они не могут быть перемещаемыми и, вероятно, более случайными, поскольку правила для неявно сгенерированных операций перемещения были выяснены просто на встрече в Питтсбурге. Открыта общая проблема с библиотекой.

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1331

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

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

Дефект перенесен в "закрытый" список. Разрешение (ссылка для удобства):

Просмотрите часть библиотеки спецификации и включите новую добавленную функцию ядра. Перемещение специальных функций-членов (N3044).

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

Итераторы, и все остальное с простой семантикой значения, например std::duration и std::time_point, , безусловно, подвижны. Как уже упоминалось, копирование подразумевает подвижность, и если это не язык, который был бы сломан. В то время этот пост не был неправильным; скорее, он спорит о разломе незавершенного языка.

Ответ 2

Я так понимаю, вы включаете: "классы, которые реализуют перемещение как обычное копирование" в вашем списке непередвижных типов., Итераторы считаются легкими объектами, которые дешево копировать. Было бы бессмысленно назначать для них оператора перемещения. Например. std::vector<T>::iterator по существу всего лишь обернутый T*, а их копирование столь же дешево, как и перемещение.

Ответ 3

Под "не подвижным" относительно итераторов вы, вероятно, имеете в виду, что определение класса не содержит объявленных пользователем операций перемещения. Но итераторы по-прежнему можно копировать. Таким образом, запрос на перемещение все еще работает и возвращается к копированию. Из-за этого нет причин предоставлять операции перемещения в ситуациях, когда операции перемещения выполнялись бы точно так же, как операции копирования. Для типичной реализации итератора нет ничего оптимизированного для w.r.t. перемещение.

Ответ 4

Короткий ответ

потому что они могут быть скопированы.

Длинный ответ

Мы должны сначала уточнить, что означает "движение". Машины Нью-Ньюмана не перемещают данные: ваша просто "копия". Данные копируются из памяти в другую. никогда не "двигался".

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

В более общем плане, операция, которая копирует значение (озеро, адрес, содержащийся в указателе), и уничтожает исходный, устанавливающий его на распознаваемый "недопустимый", как "движение".

В терминах С++ whe может различать разные типы объектов:

  • Те, которые содержат простое значение.
  • Те, которые содержат простой указатель или ссылку, которые "владеют" тем, что они называют.
  • Те, которые содержат просто простой указатель или ссылку, не "означают" то, что они называют.
  • Те, которые содержат огромные значения.
  • Те, которые представляют физический объект или операционную систему (более общую "платформу хостинга" ).

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

Теперь рассмотрим объекты типа 1:

int a=5; c=0;
c = a;
c = std::move(a);

что вы ожидаете от значения a после перемещения? Что насчет c = a+b? Если a и b "перемещены" в operator+?

Рассмотрим теперь объекты типа 2:

std::unique_ptr<int> pa(new int(5)), pb;
pb = std::move(pa);

Здесь есть два умных указателя (оба они будут уничтожены при выходе области) и только одно целое. В этом случае выполняется операция (delete), которая может быть выполнена только один раз, поэтому только один указатель должен сохранить "собственность" целого. Thsi - это случай, когда "копировать" бессмысленно, а move - единственная поддерживаемая операция

Теперь рассмотрим объекты типа 3:

std::list<int> lst = { 1,2,3,4 };
auto i = lst.begin();
auto j = i; 
*j = *i+5;
++i;
*i = *j;

Это имеет смысл: просто создайте список { 6,6,3,4 }. Итераторы не владеют тем, что они называют: многие из них могут ссылаться на одно и то же значение. Копия имеет смысл, но переместить нет: если мы переместим i в j (instad of copy), то no * я и ++ я wold станут более возможными.

Теперь рассмотрим объекты типа 4:

class A
{
   int m[15000000]; //15 million integers
public:
   int& operator[](unsigned x) { return m[x]; }
   const int& operator[](unsigned x) const { return m[x]; }
};

Такой огромный зверь будет проблематично выделяться в стеке в большинстве систем. Он, скорее всего, уйдет в кучу и будет владеть/ссылаться (умными) указателями. Его адрес будет перемещаться между его указателями, но сам объект не будет перемещаться. Он все еще может быть скопирован.

Есть еще один тонкий случай: когда A сам по себе является указателем на динамически выделенный огромный массив: это то же самое, что и std::vector: он движимый, поскольку сам по себе является "умным указателем", который владеет динамически распределенными данными, но могут быть также скопируемыми, так как может быть, вам нужна новая копия принадлежащих им данных.

Теперь тип cosider 5:

class window
{
private:
   HWND handle;
public:
   window() :handle(CreateWindow(....)) 
   { .... }
   ~window() { DestroyWindow(handle); }
};

Здесь экземпляр window представляет собой окно, существующее на экране. Что значит "копировать" или "перемещать"?

Это, скорее всего, случай для mutex, condition_variable и т.д., где и копирование, и перемещение отключены.