我想我能理解N4140中的§5.3.4/ 11,但是扩展的分配函数的概念对我来说是不可理解的

Bel*_*loc 6 c++ new-operator language-lawyer c++14

以下两段是从N4140复制的(重点是我的).

§5.3.4/ 11:

当一个新的表达式调用的分配功能和分配已经 被延长,则新的表达通过的空间要求的分配的功能类型的第一个参数的量std::size_t.该参数不得小于正在创建的对象的大小; 仅当对象是数组时,它可能大于正在创建的对象的大小.对于数组charunsigned char,new-expression的结果之间的差异并且分配函数返回的地址应该是任何对象类型的最严格的基本对齐要求(3.11)的整数倍,该对象类型的大小不大于正在创建的数组的大小.[注意:因为假定分配函数返回指向存储的指针,该存储适当地对齐具有基本对齐的任何类型的对象,所以这种对数组分配开销的约束允许分配字符数组的常用习惯用法,稍后将放置其他类型的对象. - 尾注]

§5.4.3/ 12

当一个新的表达式调用的分配功能和分配 被延长,其尺寸参数来分配呼叫不得超过上述指定尺寸被省略呼叫的总和,再加上大小为扩展呼叫有它未被扩展,加上在分配的内存中对齐已分配对象所需的任何填充.

我可以理解§5.3.4/ 11,但如上文§5.4.3/ 12所述,扩展的分配函数的概念对我来说是不可理解的.

Sha*_*our 6

N3664:澄清内存分配添加了这种语言,当分配不是可观察行为的一部分时,它允许实现融合分配.换句话说,可以扩展分配.

函数调用本身没有被扩展,但是函数调用和因此的分配可以被省略,并且分配可以稍后集中到更大的分配中.

该提案陈述了问题(强调我的):

由于严格阅读当前的C和C++标准可能导致人们得出结论,分配策略不应考虑任何不能从new和delete表达式序列中导出的信息.实质上,标准可以排除分配的宏观优化.

另一方面,严格阅读标准可能导致人们得出结论,实现必须对每个新表达式进行分配函数调用.该读数可以排除分配的微优化.

并建议:

我们建议用更准确地关注基本要求的措辞来取代现有的机制措辞.目的是启用某些现有编译器和内存分配器已有的行为.例如,请参阅TCMalloc

并进一步说:

实现的基本要求是它们提供可用的内存,而不是它们具有特定的分配调用序列.我们建议放宽关于新表达式的分配调用.

  1. 在某些约束内,分配调用的数量不是程序的可观察行为的一部分.这使实现能够通过避免或融合它们来减少分配调用的数量.

  2. 在避免或融合分配时,请求的空间量不会超过新表达式所暗示的空间量,除了满足对齐约束的额外填充.这意味着分配的空间量不会增加峰值分配.

因为C++类特定的内存分配器通常调整到特定的类大小,所以我们不会将这种放宽应用于那些分配器.

这可能导致完全省略分配,这在某些情况下可能会令人惊讶.我们可以看到从这个问题中得到的以下示例:

#include <new>  

int main()
{
    int* mem = new (std::nothrow) int[100];
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

clang优化了这个(通过godbolt观看它):

main:                                   # @main
    xorl    %eax, %eax
    retq
Run Code Online (Sandbox Code Playgroud)

这是允许的,因为没有可观察到的行为,因此这属于as-if规则的保护范围.