Выровненный динамический массив и интеллектуальный указатель
Мне часто нужно выровнять начало динамического массива с границей 16, 32 или 64 байта для векторизации, например, для SSE, AVX, AVX-512. Я ищу прозрачный и безопасный способ использования этого в сочетании с интеллектуальными указателями, в частности std::unique_ptr
.
Учитывая реализацию подпрограмм распределения и освобождения, скажем
template<class T>
T * allocate_aligned(int alignment, int length)
{
// omitted: check minimum alignment, check error
T * raw = 0;
// using posix_memalign as an example, could be made platform dependent...
int error = posix_memalign((void **)&raw, alignment, sizeof(T)*length);
return raw;
}
template<class T>
struct DeleteAligned
{
void operator()(T * data) const
{
free(data);
}
};
Я хотел бы сделать что-то вроде этого
std::unique_ptr<float[]> data(allocate_aligned<float>(alignment, length));
но я не мог понять, как сделать get unique_ptr
, чтобы использовать правильный Deleter
, не требуя от пользователя его указывать (что является потенциальной причиной ошибок). Альтернативой, которую я нашел, было использование псевдонима шаблона
template<class T>
using aligned_unique_ptr = std::unique_ptr<T[], DeleteAligned<T>>;
Тогда мы можем использовать
aligned_unique_ptr<float> data(allocate_aligned<float>(alignment, length));
Оставшаяся проблема заключается в том, что ничто не мешает пользователю помещать необработанный указатель в std::unique_ptr
.
Кроме того, вы видите что-то не так с этим? Есть ли альтернатива, которая менее подвержена ошибкам, но полностью прозрачна для пользователя после того, как было выполнено распределение?
Ответы
Ответ 1
Вы никогда не должны возвращать собственный указатель raw. allocate_aligned
нарушает это. Измените его, чтобы вместо этого использовать соответствующий умный указатель:
template<class T>
std::unique_ptr<T[], DeleteAligned<T>> allocate_aligned(int alignment, int length)
{
// omitted: check minimum alignment, check error
T * raw = 0;
// using posix_memalign as an example, could be made platform dependent...
int error = posix_memalign((void **)&raw, alignment, sizeof(T)*length);
return std::unique_ptr<T[], DeleteAligned<T>>{raw};
}
Таким образом, ни один клиент не может помещать необработанный указатель в неправильный интеллектуальный указатель, потому что он никогда не получает необработанный указатель. И вы предотвращаете утечку памяти из-за того, что случайно не поместили необработанный указатель в умный.
Как отметил @KonradRudolph, сам стандарт идет именно так: в С++ 14 std::make_unique
- это именно такая оболочка для простого new
.