treating memory returned by operator new(sizeof(T) * N) as an array
The C++ standards contain an open issue that underlying representation of objects is not an "array" but a "sequence" of unsigned char
objects. Still, everyone treats it as an array (which is intended), so it is safe to write the code like:
char* storage = static_cast<char*>(operator new(sizeof(T)*size));
// ...
char* p = storage + sizeof(T)*i; // precondition: 0 <= i < size
new (p) T(element);
as long as void* operator new(size_t)
returns a properly aligned value. Using sizeof
-multiplied offsets to keep the alignment is safe.
In C++17, there is a macro STDCPP_DEFAULT_NEW_ALIGNMENT, which specifies the maximum safe alignment for "normal" void* operator new(size_t)
, and void* operator new(std::size_t size, std::align_val_t alignment)
should be used if a larger alignment is required.
In earlier versions of C++, there is no such distinction, which means that void* operator new(size_t)
needs to be implemented in a way that is compatible with the alignment of any object.
As to being able to do pointer arithmetic directly on T*
, I am not sure it needs to be required by the standard. However, it is hard to implement the C++ memory model in such a way that it would not work.
Array placement-new requires unspecified overhead in the buffer?
Update
Nicol Bolas correctly points out in the comments below that this has been fixed such that the overhead is always zero for operator new[](std::size_t, void* p)
.
This fix was done as a defect report in November 2019, which makes it retroactive to all versions of C++.
Original Answer
Don't use operator new[](std::size_t, void* p)
unless you know a-priori the answer to this question. The answer is an implementation detail and can change with compiler/platform. Though it is typically stable for any given platform. E.g. this is something specified by the Itanium ABI.
If you don't know the answer to this question, write your own placement array new that can check this at run time:
inline
void*
operator new[](std::size_t n, void* p, std::size_t limit)
{
if (n <= limit)
std::cout << "life is good\n";
else
throw std::bad_alloc();
return p;
}
int main()
{
alignas(std::string) char buffer[100];
std::string* p = new(buffer, sizeof(buffer)) std::string[3];
}
By varying the array size and inspecting n
in the example above, you can infer y
for your platform. For my platform y
is 1 word. The sizeof(word) varies depending on whether I'm compiling for a 32 bit or 64 bit architecture.
How to create objects in C++ with dynamic alignment requirements?
You can do the following:
auto storage = static_cast<Foo*>(std::aligned_alloc(n * sizeof(Foo), alignment));
std::uninitialized_default_construct_n(storage, n);
auto ptr = std::launder(storage);
// use ptr to refer to Foo objects
std::destroy_n(storage, n);
free(storage);
Casting a pointer returned by std::aligned_alloc
to Foo*
is not undefined behavior. It would be UB if you tried to dereference it right after std::aligned_alloc
before std::uninitialized_default_construct_n
created Foo
objects.
Edit.
The code above is technically undefined behavior. But it seems that in C++ there is no 100% standard-conforming way to do such an allocation without UB. From the practical point to view this code is reliable and safe. std::launder(storage)
should probably be used to access Foo
objects through storage
pointer.
See this question for details and discussions.
Dynamic arrays in C++ without Undefined Behavior
If std::vector
can't, then you can't either.
But I wouldn't worry about it. This is one of those cases where people have found a problem with the wording of the standard, that technically makes an extremely common use case undefined. But your vectors still work.
Now, the key: that's not because of magic innate to std::vector
, or to some particular std::vector
implementation: it's because the compiler doesn't perform absurd optimisations that make use of this undefined behaviour that somebody only just spotted while studying the text with a fine-toothed comb.
Perhaps it'll be tidied up in a future revision, but for practical purposes you do not need to worry about it, whether you use std::vector
or you use new[]
.
Related Topics
Convert a Single Color with Cvtcolor
Do I Need to Protect Read Access to an Stl Container in a Multithreading Environment
Can C++ Have Code in the Global Scope
How to Pass a Vector of Strings to Execv
Constructor Invocation Mechanism
How to Pause a Pthread Any Time I Want
Loop Until Integer Input Is in Required Range Fails to Work with Non-Digit Character Inputs
Matlab VS C++ VS Opencv - Imresize
Opencv Unable to Set Up Svm Parameters
How to Provider User with Autocomplete Suggestions for Given Boost::Spirit Grammar
Is Std::String Size() a O(1) Operation
Initializer List Not Working with Vector in Visual Studio 2012
How to Cast a Derived Class to a Private Base Class, Using C-Style Cast
How to Enforce Copy Elision, Why It Won't Work with Deleted Copy Constructor
Why Can't I Open Avi Video in Opencv
Why Do Some Experienced Programmers Write Comparisons with the Value Before the Variable
Is the "One-Past-The-End" Pointer of a Non-Array Type a Valid Concept in C++