2015-01-28 10 views
17

Załóżmy, że mam typ szablonu parametr T.Nowe miejsce docelowe w std :: aligned_storage?

I załóżmy mam std::aligned_storage następująco:

typename std::aligned_storage<sizeof(T), alignof(T)>::type storage; 

Chcę umieszczenia NOWE T do storage.

Co jest wartością/typem wskaźnika zgodnego ze standardem, który należy przekazać nowemu operatorowi miejsca docelowego i jak mogę to wyprowadzić z storage?

new (& ???) T(a,b,c); 

Na przykład:

new (&storage) T(a,b,c); 
new (static_cast<void*>(&storage)) T(a,b,c); 
new (reinterpret_cast<T*>(&storage)) T(a,b,c); 
new (static_cast<T*>(static_cast<void*>(&storage)); 

Które z powyższych (jeśli w ogóle) są zgodne, a jeśli brak, jaki jest lepszy sposób?

Odpowiedz

30

najbardziej paranoiczne sposobem jest

::new ((void *)::std::addressof(storage)) T(a, b, c); 

Objaśnienie:

  • ::std::addressof chroni przed przeciążonej jednoargumentowego operator& na storage, który jest technicznie dopuszczone przez standard. (Chociaż żadna rozsądna implementacja tego nie uczyniłaby.) Zabezpiecza przed wszelkimi obszarami nazw (lub klasami) innego niż najwyższego poziomu o nazwie std, które mogą znajdować się w zakresie.
  • (void *) (który w tym przypadku jest static_cast) zapewnia, że ​​zadzwonisz umieszczenie operator new biorąc void * zamiast czegoś innego jak decltype(storage) *.
  • ::new pomija wszelkie umieszczenia specyficzne dla klasy operator new s, zapewniając, że połączenie zostanie przekazane do globalnego.

Razem, to gwarantuje, że wezwanie idzie do umieszczenia biblioteki operator new zrobieniu void *, i że T jest skonstruowany w którym storage jest.

W większości rozsądnych programów, choć

new (&storage) T(a,b,c); 

powinno wystarczyć.

+7

OK, +1 za czysty poziom paranoi. Jeśli kiedykolwiek rozpocznę pisanie wersji Hell ++, poproszę Cię o współpracę :-) – Angew

4

Funkcja przydział docelowe jest opisany w następujący sposób: (C++ 14 n4140 18.6.1.3)

void* operator new(std::size_t size, void* ptr) noexcept; 

Zwraca:ptr.

Uwagi: Nie celowo wykonuje żadnych innych czynności.

20.10.7.6 Tabela 57 opisuje aligned_storage<Len, Align> sposób:

Człon typedef type musi być typu POD nadaje się do wykorzystania jako niezainicjowanego przechowywania dla każdego obiektu której wielkość wynosi co najwyżej Len i którego wyrównanie jest dzielnikiem Align.

Oznacza to, że w przypadku &storage jest odpowiednio dostosowane do utrzymywania obiektu typu T. Dlatego w normalnych okolicznościach , wszystkie 4 sposoby wpisania miejsca docelowego na numer new są prawidłowe i równoważne. Używałbym pierwszego (new (&storage)) dla zwięzłości.


T.C. poprawnie wskazał w komentarzach, że program może zadeklarować przeciążenie funkcji alokacji, biorąc pod uwagę typename std::aligned_storage<sizeof(T), alignof(T)>::type*, która zostanie wybrana przez rozdzielczość przeciążania zamiast dostarczonej przez bibliotekę wersji "umieszczanie nowe".

Powiedziałbym, że jest to mało prawdopodobne w co najmniej 99,999% przypadków, ale jeśli trzeba się przed tym zabezpieczyć, użyj jednej z rzutów do void*. Bezpośredni static_cast<void*>(&storage) wystarczy.

Ponadto, jeśli jesteś paranoikiem do tego poziomu, prawdopodobnie powinieneś używać ::new zamiast tylko new, aby ominąć funkcje alokacji specyficzne dla klasy.

+1

Tylko wersja z 'void *' gwarantuje przejście do nowego miejsca w bibliotece. –

+0

Ponadto, aby być ekstra paranoikiem, ':: new'. –

Powiązane problemy