This simplifies the implementation, and it appears to be equivalent since
make_shared was allocating memory with std::allocator anyway.
Details
- Reviewers
zoecarver - Group Reviewers
Restricted Project - Commits
- rGece3e5bb8b05: [libc++] NFCI: Implement make_shared as allocate_shared with std::allocator
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
@glenjofe Taking a chance to tag you here since you've been involved in most changes to std::shared_ptr (and Peter Dimov does not appear to be on Phabricator). Was the intent that make_shared could be implemented via allocate_shared with the default allocator, or am I missing something subtle?
if you allocate memory with an allocator, you need to deallocate it with the same allocator. Not with operator delete
Indeed, however std::allocator is specified to use operator new and operator delete. Also, we already did allocate using std::allocator<>::allocate and our control block already deallocates using the provided allocator's deallocate function, so the allocate and deallocate calls are matched (both before and after the patch).
Good point, I hadn't thought about specializations. But even then, I think it's fine. User-provided specializations are allowed as long as they depend on a user-defined type, and they satisfy the requirements of the base template. Here, std::allocator::allocate clearly says:
Allocates n * sizeof(T) bytes of uninitialized storage by calling ::operator new(std::size_t)
IDK whether that's considered "a requirement", but it's close enough. In any case, if it is an issue, then it's a pre-existing condition because we were using std::allocator already, so I think this is really a NFC. I just hope we're not violating the intent of the standard, but as I think of it, I believe we couldn't change that behavior without an ABI break (because it would require changing the control block used by make_shared so that it doesn't use an allocator anymore.
My implementation Boost's does: https://github.com/boostorg/smart_ptr/blob/develop/include/boost/smart_ptr/make_shared_array.hpp
i.e. Those boost::make_shared overloads just call boost::allocate_shared with boost::default_allocator
It would be nice to do specify std::make_shared overloads in the standard as just returning std::allocate_shared with std::allocator but we would need wording for that.
As pointed out users can specialize std::allocator and some even specialize std::allocator_traits.
Thanks for the clarification of intent! I believe it's a reasonable implementation -- I can hardly imagine someone relying on std::allocator *not* being used by std::make_shared. Or at least, that would imply a very specific way of reading of the standard.