A common idiom when constructing buffers (say a ring buffer) for objects of class type T is to initialize a T* object with the address of memory obtained from std::malloc() or operator new(), and then to construct objects in that buffer on demand using placement new, using pointer arithmetic on the T pointer to traverse the block of memory.
While it seems highly unlikely that there is any compiler on which this would not work (it does work with g++ and clang++), it seems to me that strictly speaking this may have undefined behavior. This is because §8.7/4 of C++17 seems only to permit pointer arithmetic on arrays, and a block of memory returned by malloc, operator new or operator new[] is not an array - as I understand it only the new[] expression can create an array in dynamic memory, which will thereby be fully initialized at the point of construction.
This also got me thinking that the reference implementation of std::uninitialized_copy has undefined behaviour for dynamically allocated uninitialized memory, because its reference implementation in §23.10.10.4/1 of C++17 uses pointer arithmetic on the destination iterator, which would here be a pointer.
Arguably the same applies for std::uninitialized_copy if the uninitialized memory is obtained non-dynamically, say using an aligned array of unsigned char or of std::byte as permitted by §4.5/3 of C++17, because the arithmetic in §8.7/4 implies that the destination pointer type upon which the arithmetic is carried out should be that of the array element type (unsigned char or std::byte) and not the type constructed in it using placement new.
This seems surprising. Can anyone point out the flaw (if any) in this reasoning.
Yes, pointer arithmetic on a pointer returned from malloc or operator new has undefined behavior without a previous array placement-new (which itself cannot be done reliably) and so does using std::unintialized_copy on it, which is defined to behave as if this pointer arithmetic was done.
The best you can do is to create a std::byte (or unsigned char) array as storage, directly using new[], and then placement-new individual objects into that storage array, which will make these objects nested in the buffer array.
Pointer arithmetic on the storage array is well-defined, so you can reach pointers to the positions of the individual object in the storage array and with std::launder or by using the pointer returned from the placement-new you can obtain a pointer to the nested object. Even then you will not be able to use pointer arithmetic on pointers to the nested objects, because these do not form an array.
See the paper P0593R5
Implicit creation of objects for low-level object manipulation for further examples of this surprisingly undefined behavior and suggestions to make it defined in the future.
Related
When implementing certain data structures in C++ one needs to be able to create an array that has uninitialized elements. Because of that, having
buffer = new T[capacity];
is not suitable, as new T[capacity] initializes the array elements, which is not always possible (if T does not have a default constructor) or desired (as constructing objects might take time). The typical solution is to allocate memory and use placement new.
For that, if we know the number of elements is known (or at least we have an upper bound) and allocate on stack, then, as far as I am aware, one can use an aligned array of bytes or chars, and then use std::launder to access the members.
alignas(T) std::byte buffer[capacity];
However, it solves the problem only for stack allocations, but it does not solve the problem for heap alloations. For that, I assume one needs to use aligned new, and write something like this:
auto memory = ::operator new(sizeof(T) * capacity, std::align_val_t{alignof(T)});
and then cast it either to std::byte* or unsigned char* or T*.
// not sure what the right type for reinterpret cast should be
buffer = reinterpret_cast(memory);
However, there are several things that are not clear to me.
The result reinterpret_cast<T*>(ptr) is defined if ptr points an object that is pointer-interconvertible with T. (See this answer or https://eel.is/c++draft/basic.types#basic.compound-3) for more detail. I assume, that converting it to T* is not valid, as T is not necessarily pointer-interconvertible with result of new. However, is it well defined for char* or std::byte?
When converting the result of new to a valid pointer type (assuming it is not implementation defined), is it treated as a pointer to first element of array, or just a pointer to a single object? While, as far as I know, it rarely (if at all) matters in practice, there is a semantic difference, an expression of type pointer_type + integer is well defined only if pointed element is an array member, and if the result of arithmetic points to another array element. (see https://eel.is/c++draft/expr.add#4).
As for lifetimes are concerned, an object of type array unsigned char or std::byte can provide storage for result of placement new (https://eel.is/c++draft/basic.memobj#intro.object-3), however is it defined for arrays of other types?
As far as I knowT::operator new and T::operator new[] expressions call ::operator new or ::operator new[] behind the scenes. Since the result of builtin new is void, how conversion to the right type is done? Are these implementation based or we have well defined rules to handle these?
When freeing the memory, should one use
::operator delete(static_cast<void*>(buffer), sizeof(T) * capacity, std::align_val_t{alignof(T)});
or there is another way?
PS: I'd probably use the standard library for these purposes in real code, however I try to understand how things work behind the scenes.
Thanks.
pointer-interconvertibility
Regarding pointer-interconvertibility, it doesn't matter if you use T * or {[unsigned] char|std::byte} *. You will have to cast it to T * to use it anyway.
Note that you must call std::launder (on the result of the cast) to access the pointed T objects. The only exception is the placement-new call that creates the objects, because they don't exist yet. The manual destructor call is not an exception.
The lack of pointer-interconvertibility would only be a problem if you didn't use std::launder.
When converting the result of new to a valid pointer type (assuming it is not implementation defined), is it treated as a pointer to first element of array, or just a pointer to a single object?
If you want to be extra safe, store the pointer as {[unsigned] char|std::byte} * and reinterpret_cast it after peforming any pointer arithmetic.
an object of type array unsigned char or std::byte can provide storage for result of placement new
The standard doesn't say anywhere that "providing storage" is required for placement-new to work. I think this term is defined solely to be used in definitions of other terms in the standard.
Consider [basic.life]/example-2 where operator= uses placement-new to reconstruct an object in place, even though type T doesn't "provide storage" for the same type T.
Since the result of builtin new is void, how conversion to the right type is done?
Not sure what the standard has to say about it, but what else can it be other than reinterpret_cast?
freeing the memory
Your approach looks correct, but I think you don't have to pass the size.
I think your premise may be incorrect. If T is a class the default constructor should be called. However that can be blank and if your class contains all POD (plain old data) then nothing will be initialized. I actually count on this all the time because I often don't want things initialized for performance reasons.
I believe there are are a few caveats with this for global data and so forth where some things are zero initialized. But in general heap stuff isn't. You can test it and you will find there's a bunch of garbage in memory, at least when compiled in release mode. Some compilers will initialize memory in debug mode but that's done outside constructors.
For instance you can set data in a custom placement new function and if it's POD it will still be there in the constructor. Some people will argue this is UB but I think the standard says "nothing is done" for POD, which implies no initialization.
I am trying to write an allocator-aware container. Suppose I want to allocate a chunk of memory for three objects:
T* chunk = std::allocator_traits<Allocator>::allocate(allocator, 3);
(I know that allocators can have custom pointer types and therefore I should use std::allocator_traits<Allocator>::pointer; I am using raw pointers here for simplicity.)
Now I want to create an actual object at index 2. How do I do that? In particular, how do I calculate the pointer to the not-yet-existing element? The most obvious option would be the following:
std::allocator_traits<Allocator>::construct(allocator, chunk + 2, ...);
Unfortunately, chunk + 2 does not appear to be correct: according to the standard, pointer arithmetic can only be performed on pointers to array elements, and otherwise it causes undefined behavior. For the same reason, I cannot convert the pointer to std::byte* and use pointer arithmetic on that. (While std::allocator is defined to create an array in newly allocated memory, until C++20, the same requirement does not exist for custom allocators. Also, while C++20 adds some language for “implicit creation of objects”, this does not apply for earlier C++ versions.)
So how do I calculate the pointer to give as the second argument to construct without causing undefined behavior (before C++20)?
In the latest standard draft (C++20):
[tab:cpp17.allocator]
a.allocate(n) - Memory is allocated for an array of n T and such an object is created but array elements are not constructed.
allocator_traits::allocate(n) simply calls a.allocate(n).
So, given that the array is created, the pointer arithmetic is well defined.
In C++17 before the acceptance of proposal P0593R6, the wording was:
Memory is allocated for n objects of type T is created but objects are not constructed.
Prior to this change there was no well-defined way to do what you are asking unless:
We assume that the custom allocator provides the guarantee that such array is created. Problem with this is that there is no standard way to create an array without creating the objects (without the default allocator) and thus no standard way to implement such custom allocator.
We ignore the restrictions of pointer arithmetic. Theoretical problem with this is the undefined behaviour. In practice, this has not been a problem with actual language implementations.
When emplace_back() is called on std::vector instance, an object is created in a previously allocated storage. This can be easily achieved with placement-new, which is perfectly portable.
But now, we need to access the emplaced element without invoking undefined behavior.
From this SO post
I learned that there are two ways of doing this
use the pointer returned by placement-new:
auto *elemPtr = new (bufferPtr) MyType();
or, since C++17, std::launder the pointer casted from bufferPtr
auto *elemPtr2 = std::launder(reinterpret_cast<MyType*>(bufferPtr));
The second approach can be easily generalized to the case, where we have a lot of objects
emplaced in adjacent memory locations, as in std::vector. But what people did before C++17?
One solution would be to store pointers returned by placement-new in a separate dynamic array.
While this is certainly legal, I don't think it really implements std::vector [besides, it's a crazy idea to separately store all the addresses that we know already].
The other solution is to store lastEmplacedElemPtr inside std::vector, and remove an appropriate integer from it -- but since we don't really have an array of MyType objects this is probably also undefined. In fact, an example from this cppreference page claims that if we have two pointers
of the same type that compare equal, and one of them can be dereferenced safely, dereferencing the other can be still undefined.
So, was there a way to implement std::vector in a portable way before C++17?
Or maybe std::launder is indeed a crucial piece of C++ when it comes to placement-new,
that was missing since C++98?
I'm aware that this question is superficially similar to a lot of other questions on SO, but as far as I can tell none of them explains how to legally iterate over objects constructed by placement-new. In fact, this is all a bit confusing. For instance comments in the example
form cppreference documentation of std::aligned_storage
seem to suggest that there has been some change between C++11 and C++17, and a simple
aliasing-violating reinterpret_cast was legal before C++17 [without the need for std::launder]. Similarly, in the example from documentation of std::malloc
they simply do a pointer arithmetic on a pointer returned by std::malloc (after static_cast to the correct type).
By contrast, according to the answer to this SO question when it comes to placement-new and reinterpret_cast:
There have been some significant rule clarifications since C++11
(particularly [basic.life]). But the intent behind the rules hasn't
changed.
IIUC after P0593R6 and P1971R0/RU007, both merged into C++20 and considerable as defect reports against previous revisions, a portable std::vector implementation doesn't need std::launder.
First, after the allocate function of the given allocator (which might call operator new, std::malloc, or something like them) returned, a value_type[N] array (where N is equal to the requested number passed to allocate) is implicitly created within the allocated storage (thanks to P0593R6), and thus pointer arithmetic is valid. Even if the elements may be unconstructed.
Second, when we use placement-new without std::launder, the constructed objects can be treated as array elements, as new objects transparently replace the array elements even if the element type has const/reference non-static data members (thanks to P1971R0/RU007 and subsequent fixes in P2103R0/US041).
With regard to alignment, there is something I don't understand about the difference between an explicit call to T* t = (T*) ::operator new(sizeof(T)) and T* t = (T*)new char[sizeof(T)], where T is a C-like struct.
In the book "C++ Solutions Companion to The C++ Programming Language, Third Edition" (see exercise 12.9), the author says: "Note that one should not use the expression new char[n] to allocate raw memory because that memory may not satisfy the alignment requirements of a T, while an explicit call of the global operator new is guaranteed to yield storage that is sufficiently aligned for any C++ object.". This is also stated in an article I read on internet: http://www.scs.stanford.edu/~dm/home/papers/c++-new.html (see the "delete vs. delete[] and free" paragraph).
On the other hand, in my previous post Data alignment in C++, standard and portability a guy quoted the standard, plus a note at the end of the quote which contradicts the above people:
"A new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array. For arrays of char and unsigned char, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement (3.11) of any object type whose size is no greater than the size of the array being created. [ Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed. — end note ]"
I am confused. Could you please tell me who is right here according to the standard and, in case, why is ::operator new() different from new char[]?
That's right, new char[n] delegates to operator new[](size_t), see 5.3.4 "New" §8:
A new-expression obtains storage for the object by calling an allocation function [...]
If the allocated type is an array type, the allocation function's name is operator new[]
Hence, there is absolutely no way the former could somehow yield "less aligned" memory than the latter.
Sadly, when it comes to technical details, most C++ books out there just plain suck.
C++98 §3.7.3.1/2, about allocation functions:
The pointer returned shall be suitably aligned so that it can be converted to a pointer of
any complete object type and then used to access the object or array in the storage allocated
Together with your quote of §5.3.4/10 about new-expressions, "A new-expression passes the amount...", this means new char[n] can't offer weaker alignment guarantees, it can't be less aligned.
Cheers & hth.,
The confusion is between different versions of the standard. The older standard did not make the guarentee about new char[n] and the newer one does.
Note the bit in both cases where new char[n] might NOT return the same pointer it got back from the underlying operator new(size_t) -- that's because when allocating an array, the runtime may use the space at the beginning of the allocated block (the space between what operator new(size_t) returned and what new char[n] returns) to store the size of the array -- and that size might not require the full worst-case alignment padding. An implementation is not required to do this -- it might note that, since char has no destructor, it doesn't need to record the size of the array to know how many destructor calls to make when delete[] is called. But an implementation is free to treat all arrays uniformly.
Say I have the following C++:
char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;
Is this safe according to the C++ standard? Do I need to cast back to a char* and then use delete[]? I know it'll work in most C++ compilers, because it's plain-ordinary-data, with no destructors. Is it guaranteed to be safe?
It's not guaranteed to be safe. Here's a relevant link in the C++ FAQ lite:
[16.13] Can I drop the [] when deleting array of some built-in type (char, int, etc.)?
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.13
No, it's undefined behaviour - a compiler could plausibly do something different, and as the C++ FAQ entry that thudbang linked to says, operator delete[] might be overloaded to do something different to operator delete. You can sometimes get away with it, but it's also good practice to get into the habit of matching delete[] with new[] for the cases where you can't.
I highly doubt it.
There are a lot of questionable ways of freeing memory, for example you can use delete on your char array (rather than delete[]) and it will likely work fine. I blogged in detail about this (apologies for the self-link, but it's easier than rewriting it all).
The compiler is not so much the issue as the platform. Most libraries will use the allocation methods of the underlying operating system, which means the same code could behave differently on Mac vs. Windows vs. Linux. I have seen examples of this and every single one was questionable code.
The safest approach is to always allocate and free memory using the same data type. If you are allocating chars and returning them to other code, you may be better off providing specific allocate/deallocate methods:
SOME_STRUCT* Allocate()
{
size_t cb; // Initialised to something
return (SOME_STRUCT*)(new char[cb]);
}
void Free(SOME_STRUCT* obj)
{
delete[] (char*)obj;
}
(Overloading the new and delete operators may also be an option, but I have never liked doing this.)
C++ Standard [5.3.5.2] declares:
If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion
function, and the converted operand is used in place of the original operand for the remainder of this section. In either
alternative, the value of the operand of delete may be a null pointer value. If it is not a null pointer value, in the first
alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a
subobject (1.8) representing a base class of such an object (clause 10). If not, the behavior is undefined. In the second
alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous
array new-expression.77) If not, the behavior is undefined. [ Note: this means that the syntax of the delete-expression
must match the type of the object allocated by new, not the syntax of the new-expression. —end note ] [ Note: a pointer
to a const type can be the operand of a delete-expression; it is not necessary to cast away the constness (5.2.11) of the
pointer expression before it is used as the operand of the delete-expression. —end note ]
This is a very similar question to the one that I answered here: link text
In short, no, it's not safe according to the C++ standard. If, for some reason, you need a SOME_STRUCT object allocated in an area of memory that has a size difference from size_of(SOME_STRUCT) (and it had better be bigger!), then you are better off using a raw allocation function like global operator new to perform the allocation and then creating the object instance in raw memory with a placement new. Placement new will be extremely cheap if the object type has no constructor.
void* p = ::operator new( cb );
SOME_STRUCT* pSS = new (p) SOME_STRUCT;
// ...
delete pSS;
This will work most of the time. It should always work if SOME_STRUCT is a POD-struct. It will also work in other cases if SOME_STRUCT's constructor does not throw and if SOME_STRUCT does not have a custom operator delete. This technique also removes the need for any casts.
::operator new and ::operator delete are C++'s closest equivalent to malloc and free and as these (in the absence of class overrides) are called as appropriate by new and delete expressions they can (with care!) be used in combination.
While this should work, I don't think you can guarantee it to be safe because the SOME_STRUCT is not a char* (unless it's merely a typedef).
Additionally, since you're using different types of references, if you continue to use the *p access, and the memory has been deleted, you will get a runtime error.
This will work OK if the memory being pointed to and the pointer you are pointing with are both POD. In this case, no destructor would be called anyhow, and the memory allocator does not know or care about the type stored within the memory.
The only case this is OK with non-POD types, is if the pointee is a subtype of the pointer, (e.g. You are pointing at a Car with a Vehicle*) and the pointer's destructor has been declared virtual.
This isn't safe, and non of the responses so far have emphasized enough the madness of doing this. Simply don't do it, if you consider yourself a real programmer, or ever want to work as a professional programmer in a team. You can only say that your struct contains non destructor at the moment, however you are laying a nasty possibly compiler and system specific trap for the future. Also, your code is unlikely to work as expected. The very best you can hope for is it doesn't crash. However I suspect you will slowly get a memory leak, as array allocations via new very often allocate extra memory in the bytes prior to the returned pointer. You won't be freeing the memory you think you are. A good memory allocation routine should pick up this mismatch, as would tools like Lint etc.
Simply don't do that, and purge from your mind whatever thinking process led you to even consider such nonsense.
I've changed the code to use malloc/free. While I know how MSVC implements new/delete for plain-old-data (and SOME_STRUCT in this case was a Win32 structure, so simple C), I just wanted to know if it was a portable technique.
It's not, so I'll use something that is.
If you use malloc/free instead of new/delete, malloc and free won't care about the type.
So if you're using a C-like POD (plain old data, like a build-in type, or a struct), you can malloc some type, and free another. note that this is poor style even if it works.