"Гарантированное копирование" (P0135, С++ 1z) потенциально может потребовать поломки ABI?

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html

Вышеупомянутое предложение "Гарантированное копирование прав человека" было проголосовано в рабочем документе С++ на совещании в июне 2016 года в Оулу, Финляндия, которое затем было проголосовано за публикацию в качестве проекта комитета. Надеюсь, это приведет к публикации в качестве стандарта С++ 17 в следующем году.

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

Мой вопрос "может ли это новое требование нарушить совместимость с ABI для компиляторов, которые ранее не могли бы выполнить копирование в этих обстоятельствах или внедрили его таким образом, который не будет совместим с новыми требованиями?"

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

Ответы

Ответ 1

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

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

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

Гарантированное копирование означает, что если принимающая сторона инициализирует объект того же типа, то получатель не будет учитывать, имеет ли объект конструктор копирования/перемещения. Таким образом, код, вызывающий функцию типа auto t = Func();, не будет рассматривать ее как потенциальную операцию копирования в t. Компилятор, обрабатывающий этот код, вызовет Func с памятью возвращаемого значения, которая находится в пространстве стека для t.

И на стороне вызываемого абонента, если вы сразу возвращаете prvalue, тогда нет необходимости, чтобы существовал конструктор copy/move. Вызов будет строить prvalue непосредственно в памяти возвращаемого значения.

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

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

ABI определяет интерфейс; что вы делаете с этим интерфейсом, зависит от вас.

В качестве примера рассмотрим Itanium ABI по значениям возврата. Он позволяет хранить типы классов в реестрах, но только если они имеют тривиальные конструкторы копирования/перемещения. В противном случае, независимо от их содержимого, они должны быть сконструированы в памяти, предоставляемой вызывающей функцией. Если класс тривиально можно копировать, то вы не можете определить разницу между elision и non-elision.

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

Может ли такой ABI существовать? У меня нет особых знаний, чтобы сказать, что это невозможно. Имеет ли это? Я скорее сомневаюсь в этом, так как такой ABI сделает вообще довольно сложно.