template<class T, class D>
struct var_array_at_end {
var_array_at_end( std::size_t N ) {
::new( (void*)data() ) std::aligned_storage_t<sizeof(T)*N, alignof(T)>;
for (std::size_t i = 0; i < N; ++i) {
::new( (void*)( data()+sizeof(T)*i) ) ) T();
}
}
char* data() { return reinterpret_cast<char*>(this)+sizeof(D); }
char const* data() const { return reinterpret_cast<char*>(this)+sizeof(D); }
T* ptr(std::size_t i = 0) { return reinterpret_cast<T*>( data()+sizeof(T)*i ); }
T const* ptr(std::size_t i = 0) const { return reinterpret_cast<T*>( data()+sizeof(T)*i ); }
T& operator[](std::size_t n) {
return *ptr(n);
}
T const& operator[](std::size_t n) const {
return *ptr(n);
}
};
struct MyData:
var_array_at_end<int, MyData>
{
private:
explicit MyData( int count ):
var_array_at_end<int, MyData>( count>0?(unsigned)count:0 ),
noOfItems(count)
{}
struct cleanup {
void operator()(MyData* ptr) {
char* buff = reinterpret_cast<char*>(ptr);
ptr->~MyData();
delete[] buff;
}
};
public:
using up = std::unique_ptr<MyData*, cleanup>;
static up create( int count ) {
if (count < 0) count = 0;
std::unique_ptr<char> buff = std::make_unique<char[]>( sizeof(MyData)+sizeof(int)*count );
auto* ptr = ::new( (void*)buff.get() ) MyData( count );
(void)buff.release();
return up( ptr, {} );
}
int noOfItems;
};
MyData * getData(int size)
{
return MyData::create(size).release(); // dangerous, unmanaged memory
}
Я считаю, что это соответствует стандарту, если ваша реализация не добавляет заполнение для массивов тривиальных типов (например, char).Я не знаю ни о какой реализации.
Я не предполагал, что MyData
содержит только простые старые данные;Вы могли бы вставить std::vector
в это с вышеупомянутым.Я мог бы упростить несколько строк с этим предположением.
Это больше, чем немного боли.
auto foo = MyData::create(100)
создает уникальный ptr для MyData
, который имеет буферпосле 100 int
с.(*foo)[77]
обращается к 77-му элементу буфера.
Из-за дефекта в стандарте у вас нет массива после MyData
, а скорее буфер, содержащий 100 различных int
объектов в смежной памятиместах.Есть очень раздражающие различия между этими двумя вещами;арифметика наивного указателя гарантированно работает внутри массивов, но не между упакованными смежными int
s в буфере.Я не знаю о компиляторе, который обеспечивает это различие.