Подтвердить что ты не робот

Что такое "Обычный тип" в контексте семантики перемещения?

Alex Stepanov определил Regular Types как типы, удовлетворяющие определенным свойствам при копировании и равенстве. Теперь, когда С++ 11 добавила семантику перемещения в область общего программирования, определение Степанова больше не завершено. Я ищу хорошую ссылку на обычные типы, которые включают их взаимодействие с семантикой перемещения.

4b9b3361

Ответ 1

Резюме:

Для С++ 11 я бы включил:

  • move-ctor (noexcept)
  • move-assign (noexcept)
  • общий порядок (оператор <() для натурального общего порядка и std:: меньше < > если естественный общий порядок не существует).
  • хэш < >

И удалит:

  • swap() (non-throwing) - заменяется операциями перемещения.

Комментарий

Алекс пересматривает концепцию обычного типа в Элементах программирования. Фактически, большая часть книги посвящена регулярным типам.

Существует набор процедур, включение которых в вычислительную базис типа позволяет помещать объекты в структуры данных и использовать алгоритмы для копирования объектов из одной структуры данных в другую. Мы называем типы, имеющие такой базисный регулярный, поскольку их использование гарантирует регулярность поведения и, следовательно, интероперабельность. Раздел 1.5 EoP

В EoP Алекс вводит понятие basic_type, которое дает нам алгоритм замены без метания, который можно использовать для перемещения. Шаблон basic_type не реализуется на С++ в любом особенно полезном mannor, но вы можете использовать не-throwing (noexcept) move-ctor и move-assign как разумные аппроксимации (базовый тип позволяет перемещаться в/из временного без дополнительного уничтожения для временного). В С++ 03 обеспечение не-бросания swap() было рекомендуемым способом аппроксимировать операцию перемещения, если вы предоставили move-ctor и move-assign, тогда будет использоваться стандартная std:: swap() (хотя вы все равно можете реализовать более эффективный).

в записи, рекомендуя использовать один оператор присваивания, передавая его по значению, для покрытия как назначения назначения, так и назначения копирования. К сожалению, текущие языковые правила для того, когда тип получает по умолчанию move-ctor, заставляет это сломаться с составными типами. Пока это не будет исправлено на языке, вам нужно будет написать два оператора присваивания. Тем не менее, вы все равно можете использовать pass by value для других аргументов приемника, чтобы избежать комбинаторики при обработке move/copy для всех аргументов. ]

Алекс также добавляет требование полного упорядочения (хотя, возможно, не существует естественного общего порядка, и порядок может быть чисто репрезентативным). оператор <() должен быть зарезервирован для естественного общего упорядочения. Мое предложение состоит в том, чтобы специализировать std:: less < > (), если естественное полное упорядочение недоступно, есть прецедент для этого в стандарте).

В EoP Alex расслабляет требования к равенству, чтобы обеспечить достаточное представительное равенство. Полезное уточнение.

Регулярный тип также должен быть эквационально полным (то есть оператор ==() должен быть реализован как функция, отличная от друга, не являющейся членом). Тип, который является эквационально полным, также сериализуем (хотя и без канонического формата сериализации, реализация потоковых операторов малопригодна, за исключением отладки). Тип, который является эквационально полным, также может быть хэширован. В С++ 11 (или с TR1) вы должны указать специализацию std:: hash.

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

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

Ответ 2

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

T b = a;

и

T b = ra;

где a - это lvalue с типом T или const T, а ra - это rvalue с типом T или const T. (С аналогичными пост-условиями.)

Эта формулировка в духе статьи, я считаю. Обратите внимание, что С++ 03 уже использует понятия, такие как lvalues ​​и rvalues, так что выражение, которое мы выражали, требует, чтобы что-то вроде T source(); T b = source(); было допустимым - что-то, что кажется разумным.

При этих ограничениях, не так много изменений с С++ 11. Особо следует отметить, что такой (патологический) тип нерегулярен:

struct irregular {
    irregular() = default;
    irregular(irregular const&) = default;
    irregular& operator=(irregular const&) = default;

    irregular(irregular&&) = delete;
    irregular& operator=(irregular&&) = delete;
};

потому что что-то вроде irregular a; irregular b = a; действует, а irregular source(); irregular b = source(); - нет. Это тип, который может быть скопирован (например, назначается копировать), но недостаточно. [Это считалось некоторым дефектом и предполагается, что он будет изменен для С++ 1y, где такой тип фактически будет скопирован. ]

Идя далее, для пост-условия, что копия должна быть в некотором смысле эквивалентна оригиналу (или, для rvalues, оригиналу перед копией) для удерживания, специальный элемент перехода может быть только когда-либо "оптимизацией" 'соответствующего экземпляра специальной копии. Другой способ сказать, что семантика копирования - это уточнение семантики перемещения. Это означает, что утверждение должно выполняться в следующем:

T a;
T b = a;
T c = std::move(a);
assert( b == c );

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

Интересен тот факт, что такие черты, как std::is_copy_constructible, назывались std::has_copy_constructor, но были переименованы, чтобы уделить внимание выражениям, а не внутренним свойствам: что-то вроде std::is_copy_constructible<int>::value && std::is_move_assignable<int>::value истинно независимо от того, что int не имеет конструкторов или операторов присваивания.

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

Ответ 3

добавьте назначение перемещения и конструктор копии перемещения вместе со всеми другими операторами встроенных типов, и я бы сказал, что у вас это есть, согласно статье Степанова.