【发布时间】:2013-07-08 09:50:33
【问题描述】:
默认位置 new 运算符在 18.6 [support.dynamic] ¶1 中声明,具有不引发异常规范:
void* operator new (std::size_t size, void* ptr) noexcept;
这个函数除了return ptr;什么都不做,所以它是noexcept是合理的,但是根据5.3.4 [expr.new]¶15这意味着编译器必须检查它之前没有返回null调用对象的构造函数:
-15-
[注意: 除非使用非抛出异常规范 (15.4) 声明分配函数,否则它会通过抛出std::bad_alloc异常来指示分配存储失败(第 15 条,第 18.6.2.1 条);否则它返回一个非空指针。如果分配函数声明为不抛出异常规范,则返回 null 以指示分配存储失败,否则返回非空指针。 —尾注]如果分配函数返回null,则不进行初始化,不调用deallocation函数,new-expression的值为null。
在我看来(特别是对于放置 new,不是一般情况)这个空检查是一个不幸的性能损失,尽管很小。
我一直在调试一些代码,其中放置 new 被用于对性能非常敏感的代码路径中,以改进编译器的代码生成,并在程序集中观察到 null 检查。通过提供一个特定于类的放置 new 重载,该重载使用抛出异常规范(即使它不可能抛出)声明,条件分支被删除,这也允许编译器为周围的内联函数生成更小的代码.说放置new 函数可以抛出,即使它不能,结果是明显更好的代码。
所以我一直想知道对于放置new 案例是否真的需要空检查。它可以返回 null 的唯一方法是如果你将它传递给 null。虽然写是可能的,而且显然是合法的:
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
我不明白为什么这会有用,我建议如果程序员在使用位置 new 之前必须明确检查 null 会更好,例如
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
有没有人需要放置new 来正确处理空指针情况? (即没有添加明确的检查 ptr 是一个有效的内存位置。)
我想知道禁止将空指针传递给默认位置new 函数是否合理,如果不是,是否有更好的方法来避免不必要的分支,而不是试图告诉编译器值不为空,例如
void* ptr = getAddress();
(void) *(Obj*)ptr; // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();
或者:
void* ptr = getAddress();
if (!ptr)
__builtin_unreachable(); // same, but not portable
Obj* obj = new (ptr) Obj();
注意这个问题被有意标记为微优化,我不建议您为所有类型重载放置 new 以“提高”性能。这种影响在一个非常具体的性能关键案例中被注意到,并且基于分析和测量。
更新:DR 1748 将使用带有新位置的空指针设为未定义行为,因此不再需要编译器进行检查。
【问题讨论】:
-
啊,对并发编辑感到抱歉。在您使用条件运算符的表达式中,您是否忘记了placement new 的
(ptr)部分? -
与在调用构造函数之前让placement new 执行检查相比,在使用placement new 之前检查null 有何改进?这是同一张支票——只是在不同的时刻。要么您在某处进行了空值检查,要么您冒着调用构造函数的风险来获取空值。该标准试图避免后者。
-
@SanderDeDycker 不同之处在于,如果您知道指针不为空,则可以在调用placement new 之前留下支票。由于该标准的理念之一是“不要为不需要的东西买单”,我认为无条件强制检查是标准中的缺陷。
-
@ArneMertz, JonathanWakely :可能有点过分了,但程序员知道指针不为空的唯一方法是,如果代码保证它不能为空(例如::静态分配,计算结果,代码中存在事先检查,...)。在所有这些情况下,编译器也可以确定它不能为空(与程序员一样),并且可能会在构造函数调用之前优化空检查。我没有任何编译器走到这一步的例子,但这样做会将标准的安全角度与您的性能角度结合起来。
-
@SanderDeDycker,可能是因为函数调用
abort()如果它无法获取内存,但它是在不同的翻译单元中定义的,我没有使用 LTO。编译器如何知道我所知道的一切?是 NSA 写的吗?
标签: c++ micro-optimization placement-new noexcept