I assume your memory is aligned enough for your T
You probably want to check this out.
The next problem is exceptions. We really have to write two versions, one of which may be that the construction will throw an exception, and one without it.
I will write a safe version for an exception.
template<class T, class...Args> T* construct_n_exception_safe( std::size_t n, void* here, Args&&...args ) { auto ptr = [here](std::size_t i)->void*{ return static_cast<T*>(here)+i; }; for( std::size_t i = 0; i < n; ++i ) { try { new(ptr(i)) T(args...); } catch( ... ) { try { for (auto j = i; j > 0; --j) { ptr(j-1)->~T(); } } catch ( ... ) { exit(-1); } throw; } } return static_cast<T*>(here); }
and safe version with no exceptions:
template<class T, class...Args> T* construct_n_not_exception_safe( std::size_t n, void* here, Args&&...args ) { auto ptr = [here](std::size_t i)->void*{ return static_cast<T*>(here)+i; }; for(std::size_t i = 0; i < n; ++i) { new (ptr(i)) T(args...); } return static_cast<T*>(here); }
You can make a tag-based system to choose between them, depending on whether the T
construct is built from Args&...
or not. If it is thrown, and ->~T()
is nontrivial, use the safe option.
C ++ 17 provides some new features to perform just these tasks. They probably handle corner cases I don't have.
If you are trying to emulate new[]
and delete[]
, if T
has a non-trivial dtor, you will need to inject the amount of T
that you created in the block.
A typical way to do this is to ask for an extra room for an account in front of the unit. Those. ask sizeof(T)*N+K
, where K
could be sizeof(std::size_t)
.
Now in your emulator new[]
, the material is N
in the first bit, and then call construct_n
on the block immediately after it.
In delete[]
subtract sizeof(std::size_t)
from the one passed in the pointer, read N
, and then destroy the objects (from right to left to create a mirror construction order).
All this will require careful try
- catch
.
If, however, ~T()
trivial, your emulated new[]
and delete[]
do not save this extra std::size_t
and do not read it.
(Note that this is how to emulate new[]
and delete[]
. How exactly new[]
and delete[]
depends on the implementation. I just draw one way that you can simulate it, it may not be so compatible with that how they work on your system, for example, some ABIs can always store N
, even if ->~T()
trivial or many other options.)
As noted by the OP, you can also check the trivial design before worrying about it above.