Выравнивание статического массива с помощью std::aligned_storage

Я пытаюсь реализовать 16-байтовое выравнивание простого статического массива, используя шаблон std::aligned_storage:

#include <type_traits>
int main()
{
    const size_t SIZE = 8;
    using float_16 = std::aligned_storage<sizeof(float) * SIZE, 16>::type;
    float_16 mas;
    new(&mas) float[SIZE];//Placement new. Is this necessary? 

    mas[0]=1.f;//Compile error while attempting to set elements of aligned array
}

Я получаю следующую ошибку компиляции:

нет совпадений для «operator[]» в «mas[0]»

Затем я попытался использовать явное приведение указателя:

float* mas_ = reinterpret_cast<float*>(mas); 

но это также приводит к ошибке компиляции:

недопустимое приведение типа «float_16 {aka std::aligned_storage‹32u, 16u>::type}» к типу «float*»

Может ли кто-нибудь предложить мне, как правильно выровнять статический массив с помощью std::aligned_storage?


person gorill    schedule 05.09.2013    source источник
comment
Почему бы вам не использовать указатель, возвращаемый new?   -  person avakar    schedule 05.09.2013
comment
Указатели @avakar, возвращаемые new, не имеют расширенного выравнивания.   -  person R. Martinho Fernandes    schedule 05.09.2013
comment
@R.MartinhoFernandes: Я думаю, что авакар говорит о новом размещении (которое выровнено). (смотри мой ответ)   -  person Jarod42    schedule 05.09.2013
comment
@R.MartinhoFernandes, Jarod42 прав, я, естественно, говорил о новом размещении в исходном вопросе.   -  person avakar    schedule 06.09.2013


Ответы (3)


Вы можете использовать:

float* floats = new (&mas) float[SIZE];

и тогда вы можете использовать:

floats[0] = 1.f;

reinterpret_cast вообще нет :)

person Jarod42    schedule 05.09.2013
comment
Я бы беспокоился об использовании нового размещения для выделения массива из-за дополнительного пространства, которое может понадобиться выделению, чтобы отслеживать размер выделения, который, на самом деле, определяется реализацией. - person Fabio A.; 27.03.2017
comment
@FabioA.: Действительно, чтобы быть в полной безопасности, мы должны зациклиться, чтобы сделать новое размещение SIZE :( или использовать std::array<float, SIZE> или подобное. - person Jarod42; 27.03.2017

mas не является указателем. reinterpret_cast должен включать исключительно указатели, ссылки или целочисленные типы и только в некоторых комбинациях: указатели на целочисленные типы и из них, указатели на указатели, ссылки на ссылки или целочисленный тип на самого себя. В этом случае вы пытаетесь применить std::aligned_storage<32u, 16u>::type к указателю. Лучшее, что вы можете получить от этого, это ссылка на приведение указателя, но это не разрешено.

Вместо этого попробуйте привести его адрес к другому типу указателя: reinterpret_cast<float*>(&mas);.


для удовольствия: худшее, что вы могли бы получить, было бы, если бы std::aligned_storage<32u, 16u>::type был типом указателя. Сомнительно, т.к. 32-байтовые указатели не распространены, но для std::aligned_storage<8u, 8u>::type такое могло случиться, например, в очень противной стандартной библиотеке. Назовем это Ад++. Таким образом, в Hell++ он будет компилироваться нормально, и вы в конечном итоге приведете тип указателя к другому типу указателя, а затем проделаете с ним все гадости, такие как разыменование. Это было бы катастрофой, потому что если бы std::aligned_storage<32u, 16u>::type был типом указателя, объекты не имели бы адреса хранилища, но вместо этого они были бы являться хранилищем.

person R. Martinho Fernandes    schedule 05.09.2013
comment
Это работает, но могу ли я быть уверен, что mas_ действительно выровнен по 16 байтам? - person gorill; 05.09.2013
comment
@gorill, если это не так, сообщите об ошибке для реализации вашей стандартной библиотеки. (вы можете проверить это, переинтерпретировав указатель как целое число и проверив, делится ли он на 16) - person R. Martinho Fernandes; 05.09.2013
comment
reinterpret_cast<float*&>(mas) будет интерпретировать битовый шаблон в mas как указатель - это не может быть тем, что предполагалось. Вместо этого используйте reinterpret_cast<float*>(&mas). - person Sebastian Redl; 05.09.2013
comment
@SebastianRedl о, правда! Ему нужен массив, а не указатель. Упс. Спасибо. (И теперь я чувствую себя глупо, опубликовав эту заметку и вызвав то же самое здесь) - person R. Martinho Fernandes; 05.09.2013
comment
@Р. Мартиньо Фернандес, я попробовал ваше предложение, но получил ошибку времени выполнения. Пожалуйста, смотрите дополнительную часть в моем вопросе. - person gorill; 05.09.2013
comment
О, пожалуйста. Я буду прямолинеен. Вам нужно немного изучить основы C++, прежде чем вы начнете возиться с reinterpret_cast и aligned_storage. - person R. Martinho Fernandes; 05.09.2013
comment
Рекомендуемое чтение: stackoverflow.com/a/6445794/46642 и stackoverflow.com/q/5870038/46642 и кое-что о выходных параметрах или возвращаемых значениях. - person R. Martinho Fernandes; 05.09.2013
comment
@ R. Martinho Fernandes, извините - я действительно облажался)))) это моя глупая и очевидная ошибка. - person gorill; 05.09.2013

Просто сделать

alignas(16) float mas[SIZE];

std::aligned_storage — это реликвия C++03, исходящая от boost.

person user1095108    schedule 05.09.2013
comment
@gorill Да, 4.7 не поддерживает это. Попробуйте обновить компилятор до версии 4.8. - person user1095108; 06.09.2013
comment
aligned_storage не является пережитком C++03. Он выравнивает необработанное хранилище, а alignas выравнивает объекты. Это разные цели. Хотя в данном конкретном случае это не имеет большого значения, aligned_storage обеспечивает неизбыточную функциональность. - person R. Martinho Fernandes; 06.09.2013
comment
@R.MartinhoFernandes Нет ничего, что нельзя было бы заменить на typedef или using. - person user1095108; 06.09.2013