Когда функция вызывается, функция должна вернуть значение. Это значение требует памяти, чтобы жить, но возвращаемое значение должно выжить вне самой функции. ABI определяет, как все это работает. Вообще говоря, это происходит от вызывающего, предоставляющего кусочек памяти размера/выравнивания для возвращаемого значения функции.
Так что, если функция вычисляет значение и возвращает его, она должна (теоретически) копировать это вычисленное значение в память возвращаемого значения. И когда вызывающий абонент извлекает его, он должен (теоретически) копировать эту память возвращаемого значения в некоторый другой объект стека для последующего использования.
Негарантированное копирование означает, что ни одна из этих копий не требуется. На стороне возвращающей функции компилятору разрешено просто использовать память возвращаемого значения внутри, генерируя это значение, поэтому оператору return ничего не нужно копировать. И на принимающей стороне, если память будет использоваться для инициализации объекта стека, тогда ее не нужно копировать в эту память.
Гарантированное копирование означает, что если принимающая сторона инициализирует объект того же типа, то приемник не будет учитывать, имеет ли объект конструктор копирования/перемещения. Таким образом, код, вызывающий такую функцию, как auto t = Func();
, не будет рассматривать его как возможную операцию копирования в t
. Обработка компилятором этого кода вызовет Func
с памятью возвращаемого значения, которая находится в пространстве стека для t
.
И на стороне вызываемого лица, если вы сразу возвращаете prvalue, тогда нет необходимости в создании конструктора copy/move. Вызов будет строить prvalue непосредственно в памяти возвращаемого значения.
Вот что: ABI не заботятся ни о чем из этого. Все ABI заботится о низкоуровневой памяти. То есть, до тех пор, пока вызывающая сторона передает память возвращаемого значения соответствующего размера и выравнивания, а вызывающая сторона инициализирует эту память с объектом соответствующего типа ... ABI не заботится.
Если вызывающий абонент хочет использовать эту память возвращаемого значения для последующих операций, это нормально для ABI. Если вызываемый пользователь хочет инициализировать данные непосредственно в память возвращаемого значения вместо копирования, ABI не заметит.
ABI определяет интерфейс; то, что вы делаете с этим интерфейсом, зависит от вас.
В качестве примера рассмотрим Itanium ABI on return values. Он позволяет хранить типы классов в регистре, но только, если у них есть тривиальные конструкторы копирования/перемещения. В противном случае, независимо от их содержимого, они должны быть сконструированы в памяти, предоставляемой вызывающей функцией. Если класс тривиально можно копировать, то вы не можете определить разницу между elision и non-elision.
Единственный способ, с помощью которого ABI может создать проблему для этой функции, - это если ABI произвольно определил, где хранятся значения возврата (и, предположительно, параметры), относительно друг друга. То есть, ABI заставляет вызывающего объекта поместить объект в стек в определенном месте относительно параметров.
Может ли такой ABI существовать? У меня нет особых знаний, чтобы сказать, что это невозможно. Имеет ли это? Я скорее сомневаюсь в этом, так как такой ABI сделает вообще довольно сложно.
Я могу представить себе ABI (если это смешно), который будет нарушен этим изменением. Достаточно ли этого, или вы ищете фактические экземпляры ABI, которые фактически используются фактическим компилятором, не являющимся игрушкой, который ломается? – Yakk
Как вы можете сломать то, чего не существует? – user3104201
"* Я думаю о таких вещах, как инициализации, которые позволяют копировать, когда создание объекта может быть встроено, но не при пересечении границ единицы компиляции. *« Я не знаю таких обстоятельств, когда это было бы возможно. –