Когда функция вызывается, функция должна вернуть значение. Это значение нуждается в памяти, чтобы жить, но возвращаемое значение должно пережить саму функцию. ABI определяет, как все это работает. Вообще говоря, это происходит из-за того, что вызывающая сторона предоставляет часть памяти размера/выравнивания для возвращаемого значения функции.
Таким образом, если функция вычисляет значение и возвращает его, она должна (теоретически) скопировать это вычисленное значение в память возвращаемых значений. И когда вызывающий объект извлекает его, он должен (теоретически) скопировать эту память возвращаемого значения в какой-либо другой объект стека для последующего использования.
Негарантированное удаление копии говорит о том, что ни одна из этих копий не нужна. Что касается возвращающей функции, то компилятору разрешено просто использовать внутреннюю память возвращаемого значения при генерации этого значения, поэтому оператору return не нужно ничего копировать. А на принимающей стороне, если память будет использоваться для инициализации объекта стека, ему не нужно копировать в эту память.
Гарантированное удаление копии говорит о том, что если принимающая сторона инициализирует объект того же типа, то получатель не будет учитывать, есть ли у объекта конструктор копирования/перемещения. Таким образом, код, вызывающий такую функцию, как auto t = Func();
, не будет рассматривать ее как потенциальную операцию копирования в t
. Компилятор, обрабатывающий этот код, вызовет Func
с памятью возвращаемых значений, которая находится в пространстве стека для t
.
А на стороне вызываемого объекта, если вы возвращаете prvalue напрямую, то нет необходимости в существовании конструктора копирования/перемещения. Вызываемый объект создаст значение prvalue непосредственно в памяти возвращаемого значения.
Вот в чем дело: ABI не заботятся ни о чем из этого. Все, о чем заботится ABI, — это низкоуровневая память. То есть до тех пор, пока вызывающий объект передает память возвращаемого значения соответствующего размера и выравнивания, а вызываемый объект инициализирует эту память объектом соответствующего типа... ABI все равно .
Если вызывающая сторона хочет использовать эту память возвращаемых значений для последующих операций, это нормально для ABI. Если вызываемый объект хочет инициализировать данные непосредственно в памяти возвращаемых значений, а не копировать их, ABI этого не заметит.
ABI определяет интерфейс; что вы делаете с этим интерфейсом, зависит от вас.
В качестве примера рассмотрим ITanium ABI для возвращаемых значений. Это позволяет хранить типы классов в регистрах, но только, если у них есть тривиальные конструкторы копирования/перемещения. В противном случае, независимо от их содержимого, они должны быть построены в памяти, предоставленной вызывающей функцией. Если класс тривиально копируется, то вы не можете сказать разницу между элизией и не-элизионностью.
Единственный способ, которым ABI может создать проблему для этой функции, — это если ABI произвольно решает, где хранить возвращаемое значение (и, предположительно, параметры) относительно друг друга. То есть ABI заставляет вызывающую сторону поместить объект в стек в определенное место относительно параметров.
Может ли существовать такой ABI? У меня нет особых знаний, чтобы сказать, что это невозможно. Имеет ли это? Я довольно сомневаюсь в этом, поскольку такой ABI в целом затруднил бы элизию.
person
Nicol Bolas
schedule
26.06.2016