当一个函数被调用时,该函数必须返回一个值。该值需要内存才能存在,但返回值需要超过函数本身。ABI 定义了这一切是如何工作的。一般来说,这是由调用者为函数的返回值提供一块大小/对齐的内存而发生的。
因此,如果一个函数计算一个值并返回它,它必须(理论上)将该计算值复制到返回值内存中。当调用者检索它时,它必须(理论上)将该返回值内存复制到其他一些堆栈对象中以供以后使用。
非保证复制省略表示这些副本都不是必需的。在返回函数方面,允许编译器在生成该值时在内部简单地使用返回值内存,因此 return 语句不必复制任何内容。在接收端,如果内存将用于初始化堆栈对象,则不必复制到该内存中。
保证复制省略表示如果接收方正在初始化相同类型的对象,那么接收方将不会考虑该对象是否具有复制/移动构造函数。因此,调用类似函数的代码auto t = Func();
不会将其视为潜在的复制操作t
。处理该代码的编译器将Func
使用位于堆栈空间中的返回值内存调用t
.
而在被调用方,如果直接返回纯右值,则不需要存在复制/移动构造函数。被调用者将直接在返回值内存中构造prvalue。
事情是这样的:ABI 不关心这些。ABI 关心的只是低级内存。也就是说,只要调用者传递适当大小和对齐的返回值内存,并且被调用者使用适当类型的对象初始化该内存...... ABI不在乎。
如果调用者想将该返回值内存用于以后的操作,那对 ABI 来说没问题。如果被调用者想要将数据直接初始化到返回值内存而不是复制它,ABI 不会注意到。
ABI 定义接口;你用那个界面做什么取决于你。
例如,考虑返回值的 Itanium ABI。它允许将类类型存储在寄存器中,但前提是它们具有简单的复制/移动构造函数。否则,无论它们的内容如何,它们都必须在调用函数提供的内存中构造。如果该类是可简单复制的,那么您将无法区分省略和非省略之间的区别。
ABI 可能对该功能造成问题的唯一方法是,如果 ABI 任意决定相对于彼此存储返回值(以及可能是参数)的位置。也就是说,ABI 强制调用者将对象放在堆栈上相对于参数的特定位置。
这样的 ABI 会存在吗?我没有特别的知识可以说它不能。可以?我相当怀疑它,因为这样的 ABI 通常会使省略非常困难。