将空指针传递到展示位置新 [英] Passing null pointer to placement new

查看:130
本文介绍了将空指针传递到展示位置新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

默认放置 new 运算符在18.6 [support.dynamic]¶1中声明为非抛出异常规范:

The default placement new operator is declared in 18.6 [support.dynamic] ¶1 with a non-throwing exception-specification:

void* operator new (std::size_t size, void* ptr) noexcept;

此函数不执行任何操作,除了 return ptr; 所以它是合理的 noexcept ,但是根据5.3.4 [expr.new]¶15这意味着编译器必须检查它不返回null之前调用对象的构造函数:

This function does nothing except return ptr; so it is reasonable for it to be noexcept, however according to 5.3.4 [expr.new] ¶15 this means that the compiler must check it doesn't return null before invoking the object's constructor:


-15-

[注意:函数被声明为非抛出异常规范(15.4),它指示未能通过引发 std :: bad_alloc 异常来分配存储(第15,18.6.2.1节) ;它返回一个非空指针。如果分配函数使用非抛出异常规范来声明,则返回null以指示无法分配存储,否则返回非空指针。 -end note ]如果分配函数返回null,则不应进行初始化,不应调用解除分配函数,new-expression的值应为null。

-15-
[Note: unless an allocation function is declared with a non-throwing exception-specification (15.4), it indicates failure to allocate storage by throwing a std::bad_alloc exception (Clause 15, 18.6.2.1); it returns a non-null pointer otherwise. If the allocation function is declared with a non-throwing exception-specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise. —end note] If the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.

在我看来,(特别是对于 new ,不是一般的)不幸的性能损失,虽然很小。

It seems to me that (specifically for placement new, not in general) this null check is an unfortunate performance hit, albeit small.

我已经调试了一些代码,其中 new 非常性能敏感的代码路径,以提高编译器的代码生成和检查null在集合中观察到。通过提供使用throwing exception-specification声明的类特定的放置 new 重载(即使它不能抛出)条件分支被删除,这也允许编译器为周围内联函数生成较小的代码。说出 new 函数可以抛出的结果,即使它不能,是可测量更好的代码。

I've been debugging some code where placement new was being used in a very performance-sensitive code path to improve the compiler's code generation and the check for null was observed in the assembly. By providing a class-specific placement new overload that is declared with a throwing exception-specification (even though it can't possibly throw) the conditional branch was removed, which also allowed the compiler to generate smaller code for the surrounding inlined functions. The result of saying the placement new function could throw, even though it couldn't, was measurably better code.

所以我一直在想,对于 new 情况,是否真的需要空检查。它可以返回null的唯一方法是如果你传递null。虽然这是可能的,显然是合法的:

So I've been wondering whether the null check is really required for the placement new case. The only way it can return null is if you pass it null. Although it's possible, and apparently legal, to write:

void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );

我不明白为什么这将是有用的,我建议如果程序员有更好的在使用展示位置之前显式检查null new 例如

I can't see why that would be useful, I suggest it would be better if the programmer had to check for null explicitly before using placement new e.g.

Obj* obj = ptr ? new (ptr) Obj() : nullptr;

任何人都需要放置 new 处理空指针的情况? (即不添加显式检查 ptr 是有效的内存位置。)

Has anyone ever needed placement new to correctly handle the null pointer case? (i.e. without adding an explicit check that ptr is a valid memory location.)

将禁止向默认放置 new 函数传递一个空指针,如果不是,是否有一些更好的方法来避免不必要的分支,除了试图告诉编译器的值不为null例如

I'm wondering whether it would be reasonable to forbid passing a null pointer to the default placement new function, and if not whether there is some better way to avoid the unnecessary branch, other than trying to tell the compiler the value is not null e.g.

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();

NB 此问题是有意标记的微优化, >不,建议您为所有类型重载以提高性能。

N.B. This question is intentionally tagged micro-optimisation, I am not suggesting that you go around overloading placement new for all your types to "improve" performance. This effect was noticed in a very specific performance-critical case and based on profiling and measurement.

更新: DR 1748 使未定义的行为使用一个空指针与placement new,因此编译器不再需要检查。

Update: DR 1748 makes it undefined behaviour to use a null pointer with placement new, so compilers are no longer required to do the check.

推荐答案

虽然我看不到很多问题,除了有人需要放置新的正确处理空指针的情况? (我没有),我认为这种情况是有趣的,足以溢出一些关于这个问题的想法。

While I can't see much of a question in there except "Has anyone ever needed placement new to correctly handle the null pointer case?" (I haven't), I think the case is interesting enough to spill some thoughts on the issue.

我认为标准坏或不完整的wrt布局新功能,一般分配功能的要求。

I consider the standard broken or incomplete wrt the placement new function and requirements to allocation functions in general.

如果仔细观察引用的§5.3.4,13,则意味着必须针对返回的空指针检查每个分配函数,即使它不是 noexcept 。因此,应将其重写为

If you look closely at the quoted §5.3.4,13, it implies that every allocation function has to be checked for a returned nullpointer, even if it is not noexcept. Therefore, it should be rewritten to


如果分配函数使用非抛出异常规范和返回null,不应进行初始化,不应调用解除分配函数,并且new-expression的值应为null。

If the allocation function is declared with a non-throwing exception-specification and returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.

这不会损害分配函数抛出异常的有效性,因为它们必须遵守§3.7.4.1

That would not harm the validity of allocation functions throwing exceptions, since they have to obey §3.7.4.1:


[...]如果成功,它将返回一个存储块的开始地址,字节应至少与请求的大小一样大。 [...]返回的指针应适当对齐,以便它可以转换为具有基本对齐要求(3.11)的任何完整对象类型的指针,然后用于访问分配的存储中的对象或数组(直到存储通过对相应解除分配函数的调用显式释放)。

[...] If it is successful, it shall return the address of the start of a block of storage whose length in bytes shall be at least as large as the requested size. [...] The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).

§5.3.4.14


[注意:当分配函数返回一个非null值时,它必须是一个指向存储块的指针,已保留对象的空间。假定存储块被适当地对齐并且具有所请求的大小。 [...] -end note]

[ Note: when the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved. The block of storage is assumed to be appropriately aligned and of the requested size. [...] -end note ]

显然,一个刚刚返回给定指针的位置new不能合理地检查可用的存储大小和对齐。因此,

Obviously, a placement new that just returns the given pointer, cannot reasonably check avilable storage size and alignment. Therefore,

§18.6.1.3,1关于刊登位置新的说


[...](3.7.4)的规定不适用于运营商新增和运营商删除的这些保留的放置形式。

[...] The provisions of (3.7.4) do not apply to these reserved placement forms of operator new and operator delete.

(我想他们错过了在那个地方提到§5.3.4.14)

(I guess they missed to mention §5.3.4,14 at that place.)

但是,这些段落一起间接地说如果你传递一个垃圾指针到palcement函数,你得到UB,因为违反了§5.3.4.14。所以这是由你来检查任何poitner给位置新的理智。

However, together these paragraphs say indirectly "if you pass a garbage pointer to the palcement functions, you get UB, because §5.3.4,14 is violated". So it's up to you to check the sanity of any poitner given to placement new.

本着这种精神,以及重写的§5.3.4,13,标准可以剥离 noexcept 放置新,导致除了间接结论:...如果你传递null,你也得到UB。另一方面,它不太可能有一个未对齐的指针或指针到太少的内存,而不是一个空指针。

In that spirit, and with the rewritten §5.3.4,13, the standard could strip the noexcept from placement new, leading to an addition to that indirect conclusion: "...and if you pass null, you get UB as well". On the other hand, its much less likely to have a misaligned pointer or pointer to too few memory than having a null pointer.

但是,这将删除需要检查反对null,它将适合哲学不支付你不需要的。分配函数本身不需要检查,因为§18.6.1.3,1明确地这样说。

However, this would remove the need for checking against null, and it would fit well to the philosophy "don't pay for what you don't need". The allocation function itself would not need to check, because §18.6.1.3,1 explicitly says so.

为了圆整,可以考虑添加第二个重载



To round things up, one could consider adding a second overload

 void* operator new(std::size_t size, void* ptr, const std::nothrow_t&) noexcept;

很遗憾,向委员会提出此建议不太可能导致更改,依赖于放置新的正确的空指针。

Sadly, proposing this to the committee is unlikely to result in a change, because it would break existing code relying on placement new being ok with null pointers.

这篇关于将空指针传递到展示位置新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆