2015-04-02 13 views
10

Często potrzebuję wyrównać początek tablicy dynamicznej do granicy 16, 32 lub 64 bajtów dla wektorowania, np. Dla SSE, AVX, AVX-512. Szukam przejrzystego i bezpiecznego sposobu użycia tego w połączeniu z inteligentnymi wskaźnikami, w szczególności std::unique_ptr.Wyrównana tablica dynamiczna i inteligentny wskaźnik

Biorąc pod uwagę wdrożenie rozdziału i dealokacji rutyny, powiedzmy

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); 
    } 
}; 

chciałbym zrobić coś takiego

std::unique_ptr<float[]> data(allocate_aligned<float>(alignment, length)); 

ale nie mogłem dowiedzieć się, jak dostać unique_ptr używać właściwy Deleter bez konieczności podawania przez użytkownika (co jest potencjalną przyczyną błędów). Alternatywą znalazłem użyć szablonu alias

template<class T> 
using aligned_unique_ptr = std::unique_ptr<T[], DeleteAligned<T>>; 

Wtedy możemy użyć

aligned_unique_ptr<float> data(allocate_aligned<float>(alignment, length)); 

Pozostałą problemem jest to, że nic nie trzyma użytkownikowi wprowadzenie surowego wskaźnik do std::unique_ptr.

Czy widzisz coś nie tak z tym? Czy istnieje alternatywa, która jest mniej podatna na błędy, ale całkowicie przejrzysta dla użytkownika po dokonaniu alokacji?

Odpowiedz

10

Nigdy nie powinieneś zwracać posiadania surowego wskaźnika. allocate_aligned narusza to. Zmień go, aby powrócić odpowiedni wskaźnik inteligentnych zamiast:

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}; 
} 

W ten sposób klient nie może umieścić surowy wskaźnik w nieodpowiednim inteligentnego wskaźnika, bo nigdy nie dostać surowy wskaźnik w pierwszej kolejności. I zapobiegasz przypadkom, w których wycieki pamięci przypadkowo nie wprowadzają surowego wskaźnika w inteligentny.

Jak zauważył @KonradRudolph, standard sam w sobie idzie w ten sposób — w C++ 14, std::make_unique jest właśnie takim opakowaniem zwykłego new.

+2

Warto wspomnieć, że jest to zasadniczo to, co robi 'std :: make_unique' dla' new' w C++ 14. –

+0

@KonradRudolph Dobry punkt, redagowany w. – Angew

Powiązane problemy