对于编译器来说,完全可以允许(但不是必需)在您的原始示例中优化分配,甚至在按照标准§1.9的EDIT1示例中更是如此(通常称为as-if规则):
需要遵循实现以(仅)模拟抽象机的可观察到的行为,如下所述:
[3页条件]
在cppreference.com上可以找到更易于理解的表示形式。
相关要点是:
- 您没有挥发物,因此1)和2)不适用。
- 您不会输出/写入任何数据或提示用户,因此3)和4)不适用。但是即使您这样做了,他们也会在EDIT1中得到明显的满足(可以说在原始示例中也是如此,尽管从纯粹的理论角度来看,这是非法的,因为程序的流程和输出在理论上是不同的,但是请参见两段)下面)。
异常,甚至是未捕获的异常,都是定义明确(并非未定义!)的行为。但是,严格来说,如果发生new
抛出(不会发生,也请参见下一段),则可观察到的行为将有所不同,无论是程序的退出代码还是程序后面可能出现的任何输出。
现在,在特殊的小分配情况下,您可以为编译器提供“疑问的好处”,它可以保证分配不会失败。
即使在内存压力非常大的系统上,当您使用的可用分配粒度小于最小可用粒度时,甚至也无法启动进程,并且也将在调用之前设置堆main
。因此,如果此分配失败,则该程序将永远不会启动,或者main
甚至在调用之前就已经遇到了不良的结局。
就此而言,假设编译器知道这一点,即使在理论上分配可以抛出也,甚至优化原始示例也是合法的,因为编译器可以实际上保证它不会发生。
<稍微不确定>
另一方面,不允许在EDIT2示例中优化分配(如您所见,是编译器错误)。消耗该值以产生外部可观察的效果(返回码)。
请注意,如果您将替换new (std::nothrow) int[1000]
为new (std::nothrow) int[1024*1024*1024*1024ll]
(这是4TiB分配!),这在当前的计算机上肯定会失败,那么它仍然可以优化呼叫。换句话说,尽管您编写了必须输出0的代码,但它返回1。
@Yakk对此提出了一个很好的论据:只要不触摸内存,就可以返回指针,并且不需要实际的RAM。就此而言,优化EDIT2中的分配甚至是合法的。我不确定谁是对的,谁是错的。
仅仅因为操作系统需要创建页表,在没有至少2位GB的RAM的机器上进行4TiB分配几乎可以保证失败。当然,现在,C ++标准并不关心页表或操作系统正在做什么以提供内存,这是事实。
但是另一方面,“如果不触摸内存,这将起作用”这一假设确实依赖于这样的细节以及操作系统提供的内容。如果没有被碰过它实际上是不需要RAM是唯一真正的假设,因为操作系统提供的虚拟内存。这意味着操作系统需要创建页表(我可以装作自己不知道它,但是这并不能改变我仍然依赖它的事实)。
因此,我认为先假设一个然后说“但我们不在乎另一个”并不是100%正确的。
因此,是的,只要不触摸内存,编译器就可以假定通常完全可以实现4TiB分配,并且可以假定通常可以成功。它甚至可能假设它可能成功(即使不是成功)。但是我认为,无论如何,在发生故障的可能性时,您永远都不能假定必须采取某些措施。而且不仅存在故障的可能性,在该示例中,故障甚至更有可能发生。
</略有未定>