You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The constructors of std::any that create an object of type T use std::allocator to allocate memory and "::new ((T*)p)" placement new to construct the object. (That is, memory allocation subject only to specializations of std::allocator, and construction only to overloads of global operator new for user pointer types.)
By contrast, destruction uses "delete (T*)p". This means that overloaded operators T::operator delete will be used.
(In the above, the cast notation "(T*)" doesn't appear literally in the code, but is just my way of indicating the argument type. Specifically, note that there is no cast to void* in the placement new.)
This mismatch results in undefined behaviour whenever a suitable amount of user customization is present for T. Examples can be constructed to exercise all aspects of this (specializing std::allocator, adding a ::operator new(std::size_t, T*) overload, or adding overloaded operators T::operator delete).
The generated code clearly shows one call to "operator new" and one to "free".
One solution would be to make the construction use an unqualified new expression "new _Tp(...)"; this means that standard library code will call user-defined functions for internal bookkeeping. An alternative solution would be to also use std::allocator to deallocate the allocation.
The text was updated successfully, but these errors were encountered:
std::allocator::allocate is required to get its storage from ::operator new, and
std::allocator::delete is required to use ::operator delete
but there's certainly some wiggle room in those specifications.
(Deliberately, I'm sure)
Yes, this is true, but the way I read this is merely as a guarantee that, say, replacements of "::operator new" are observed by (any specialization of) std::allocator. It doesn't preclude specializations from doing additional, custom work (so the allocator should still be called in matching pairs, and not freely interchanged with direct calls to "::operator new/delete").
Extended Description
The constructors of std::any that create an object of type T use std::allocator to allocate memory and "::new ((T*)p)" placement new to construct the object. (That is, memory allocation subject only to specializations of std::allocator, and construction only to overloads of global operator new for user pointer types.)
By contrast, destruction uses "delete (T*)p". This means that overloaded operators T::operator delete will be used.
(In the above, the cast notation "(T*)" doesn't appear literally in the code, but is just my way of indicating the argument type. Specifically, note that there is no cast to void* in the placement new.)
This mismatch results in undefined behaviour whenever a suitable amount of user customization is present for T. Examples can be constructed to exercise all aspects of this (specializing std::allocator, adding a ::operator new(std::size_t, T*) overload, or adding overloaded operators T::operator delete).
One example:
================
#include
#include
#include
struct U {
int big[128];
static void* operator new(std::size_t n) { return std::malloc(n); }
static void operator delete(void* p) { std::free(p); }
};
int main() {
std::any a(std::in_place_type);
return std::any_cast<U*>(a) != nullptr; // avoid optimizing everything away
}
================
The generated code clearly shows one call to "operator new" and one to "free".
One solution would be to make the construction use an unqualified new expression "new _Tp(...)"; this means that standard library code will call user-defined functions for internal bookkeeping. An alternative solution would be to also use std::allocator to deallocate the allocation.
The text was updated successfully, but these errors were encountered: