#cpp #массивы #память #шаблоны_с++
Если я выделяю память для некоторого типа T - пусть это в шаблоне, скажем - templatechar* alloc(size_t n) { return new char[n*sizeof(T)] ; } то места гарантированно хватит для n элементов с типом T или нет? Какие-то проблемы из-за выравнивания могут быть? И если могут - то как их правильно решить? Дополнение - по совету @VladD я ппробовал выделить память с особыми требованиями к выравниванию: #include #include using namespace std; struct alignas(256) D { char a; char b; }; template void* alloc(size_t n) { return reinterpret_cast (new aligned_storage [n]); } template int allocSize() { cout << "sizeof = " << sizeof(T) << ", alignof = " << alignof( T) << endl; cout << "type = " << typeid(typename aligned_storage ::type).name() << endl; return sizeof(typename aligned_storage ::type); } int main() { cout << allocSize () << endl; cout << alloc (5) << endl; } GCC это не смог отработать - https://ideone.com/dgo75h. Но Visual Studio нормально отработал, но выдал: sizeof = 256, alignof = 256 type = union std::_Align_type 256 004A1258 т.е. понял и размер, и выравнивание, но адрес дал выровненный явно не на 256. Мне это выравнивание не нужно, это так, игры - но тем не менее получается, что так выделять память не получается?
Ответы
Ответ 1
По идее, вам нужно std::aligned_storage. Должно по идее работать так: templatechar* alloc(size_t n) { return new aligned_storage [n]; } Проблемы из-за выравнивания с простым подходом очевидны: new char[] не знает ваших требований по выравниванию, и может выделить неправильно выровненную память. Для выделения объектов в вашей памяти вам понадобится placement new. Вам должен пригодится std::align, использование которого описано здесь. Обновление: я перепроверил, и на самом деле new не соблюдает кастомные требования по выравниванию. Поэтому нужно делать выравнивание самостоятельно, при помощи std::align. Получается вот что: // подсчёт длины нужной аллокации template size_t aligned_char_size(size_t n) { size_t result = n * sizeof(T); bool is_overaligned = alignof(T) > alignof(std::max_align_t); if (is_overaligned) result += alignof(T) - 1; return result; } Применяем: struct alignas(1024) S { }; // выделёем память на 3 экземпляра size_t total_space = aligned_char_size (3); void* p = new char[total_space]; std::cout << p << std::endl; // получаем выровненный указатель при помощи std::align void* allocated_item = p; size_t remaining_space = aligned_char_size(3); if (std::align(alignof(S), sizeof(S), allocated_item, remaining_space)) { std::cout << allocated_item << std::endl; std::cout << (size_t)allocated_item % alignof(S) << std::endl; } else { std::cout << "Impossible" << std::endl; } Результат на Visual Studio 2017: 000002333DF15950 000002333DF15C00 0 Если убрать alignas(1024), получаем 000002A27FD2ECD0 000002A27FD2ECD0 0Ответ 2
Нет, проблем не будет. Во-первых, места гарантированно хватит. Во-вторых, касательно выравнивания new-expression гарантирует, что выделенная через new char[n] память выровнена по самому строгому фундаментальному требованию выравнивания. То есть память будет выровнена по alignof(std::max_align_t). Полученная от new-expression память будет правильно выровнена для хранения любых фундаментальных типов, поддерживаемых реализацией, и типов, построенных из них. 3.7.4.1 Allocation functions 2 [...] The pointer returned shall be suitably aligned so that it can be converted to a pointer to any suitable complete object type (18.6.2.1) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).[...] 5.3.4 New 11 When a new-expression calls an allocation function and that allocation has not been extended, the 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 ] Приведенная цитата открытым текстом говорит, что ограничения на структуру блока памяти, выделяемого через new char[n] были введены именно для того, чтобы обеспечить правильное выравнивание при использовании именно вашего способа выделения памяти. Поэтому беспокоиться о выравнивании вам не нужно, пока у вас не появляются какие-то специальные требования расширенного выравнивания, превосходящие максимальное фундаментальное выравнивание.
Комментариев нет:
Отправить комментарий