[libcxx-commits] [libcxx] [libc++] Add an ABI setting to harden unique_ptr<T[]>::operator[] (PR #91798)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Oct 9 10:55:09 PDT 2024


ldionne wrote:

> What about a case when a `std::unique_ptr<T[]>` with a default deleter temporarily points to a memory allocated with something but `new T[]`? If the code ensures the pointer is always reset before `std::unique_ptr` is destroyed, and the default deleter is never used. Something like in this example: https://gcc.godbolt.org/z/4fczP6e1q
> 
> If we skip the question why would one do that at all (I'm about to ask the author(s)), it seems like this is a valid use of the `std::unique_ptr<>` according to the C++20 standard. Or am I missing something?

I'm not certain, but I think it's invalid. I think we're walking on an extremely thin line here, see below.

First, about whether this *should* be valid (I don't think you disagree, I just want to write it down): `std::unique_ptr` is meant to be used for memory management, and if you perform your own memory management and only use `std::unique_ptr` as a glorified raw pointer (like in the godbolt link), there's really no point to using `std::unique_ptr` at all. In fact, that `std::unique_ptr` is really weird since `reset()` can't be called on it, it can't be destroyed, etc. Otherwise, the deleter (`std::default_delete` in this case) would be called on the pointer, and that's invalid because of how that pointer was allocated. I think it would be really unfortunate for it to be considered valid by the Standard since the intent of `std::unique_ptr` is clear, and supporting such a contrived use case would prevent us from providing value to the 99% of use cases that are normal.

Now, about whether this is actually, technically, valid. [unique.ptr#single.general-1](http://eel.is/c++draft/unique.ptr#single.general-1) says this:

> The default type for the template parameter D is default_delete[.](http://eel.is/c++draft/unique.ptr#single.general-1.sentence-1) A client-supplied template argument D shall be a function object type ([[function.objects]](http://eel.is/c++draft/function.objects)), lvalue reference to function, or lvalue reference to function object type for which, given a value d of type D and a value ptr of type unique_ptr<T, D>​::​pointer, the expression d(ptr) is valid and has the effect of disposing of the pointer as appropriate for that deleter[.](http://eel.is/c++draft/unique.ptr#single.general-1.sentence-2)

I am tempted to say that the requirements on a custom deleter's validity also apply to the default deleter. Furthermore, that may be stretching it a bit, but I would say that this requirement is not satisfied in the case you showed above because the expression `d(ptr)` does not have the effect of disposing of the pointer as appropriate [for that deleter].

To be unambiguous, we should perhaps add something to the various `std::unique_ptr` constructors that says "the pointer used to initialized the `unique_ptr` must be such that calling the deleter on it disposes of the pointer in a valid way", but we'd have to be careful not to step on the toes of `unique_ptr`'s ability to hold incomplete types.

Frankly, I'm not certain what to do with this. I feel like it would be really unfortunate to drop this hardening check on a slightly ambiguous technicality since it can deliver a ton of value in the real world. I think I'll email the reflector to see what the sentiment is like over there.

https://github.com/llvm/llvm-project/pull/91798


More information about the libcxx-commits mailing list