wyrównana pamięć masowa i ścisłe aliasowanie

Obecnie używam aligned_storage, aby zaimplementować "opcjonalny" Typ podobny do boost:: opcjonalny. Aby to osiągnąć mam takiego członka klasy:

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;

Używam placement new do tworzenia obiektu, jednak nie przechowuję nigdzie zwróconego wskaźnika. Zamiast tego uzyskuję dostęp do bazowego typu obiektu we wszystkich moich funkcjach członkowskich, takich jak ta (oczywiście z sprawdzeniami, aby upewnić się, że obiekt jest poprawny poprzez flagę logiczną przechowywaną również w moim opcjonalnym typie):

T const* operator->() const {
    return static_cast<T const*>(static_cast<void const*>(&t_));
}

Mój pytanie, czy to bezpieczne. Rozumiem, że moje użycie umieszczania nowego zmienia "typ dynamiczny" obiektu i tak długo, jak będę uzyskiwał dostęp do pamięci za pomocą tego typu, będę w porządku. Jednak nie jestem pewien, czy muszę trzymać wskaźnik zwrócony z umieszczania nowego, czy też mogę po prostu rzucić do podstawowego typu, gdy potrzebuję do niego dostępu. Przeczytałem sekcję 3.10 standardu C++11, jednak nie jestem na tyle biegły w standardach, aby być jasne.

Jeśli to możliwe, czułbym się lepiej, gdybyś mógł podać odniesienie do standardu w swojej odpowiedzi (pomaga mi spać w nocy :P).

Author: Jeffrey Bosboom, 2012-11-20

1 answers

ABICT Twoje użycie jest bezpieczne.

  • umieszczenie nowego obiektu typu T spowoduje utworzenie obiektu zaczynającego się od podanego adresu.

§5.3.4 / 10 mówi:

Nowe wyrażenie przekazuje żądaną ilość przestrzeni do funkcja alokacji jako pierwszy argument typu std:: size_t. że argument nie może być mniejszy niż rozmiar tworzonego obiektu; może być większy niż rozmiar tworzonego obiektu tylko wtedy, gdy obiekt jest / align = "left" /

Dla obiektu innego niż tablica, rozmiar przydzielonego obiektu nie może być większy niż rozmiar obiektu, więc reprezentacja obiektu musi zaczynać się na początku przydzielonej pamięci, aby się zmieścić.

Placement new zwraca przekazany wskaźnik (patrz § 18.6.1.3/2) jako wynik "alokacji", więc reprezentacja obiektu zbudowanego obiektu rozpocznie się pod tym adresem.

  • static_cast<> oraz konwersje niejawne pomiędzy typem T* a typem void* convert pomiędzy wskaźnikiem do obiektu a wskaźnikiem do jego przechowywania, jeśli obiekt jest obiektem kompletnym.

§4.10 / 2 mówi:

Wartość prvalue typu "wskaźnik do cv T", gdzie T jest typem obiektu, może być konwertowane do wartości PR typu "wskaźnik do CV void". Wynik konwersja "wskaźnika do CV T" na "wskaźnik do CV void" wskazuje na początek miejsca przechowywania, w którym znajduje się obiekt typu T, jako jeśli obiekt jest najbardziej pochodnym obiektem (1.8) typu T [...]

Definiuje to konwersję implicit do konwersji, jak podano. Dalej §5.2.9 [expr.statyczne.cast] / 4 Definiuje static_cast<> dla jawnych konwersji, gdzie istnieje konwersja implicit, która ma taki sam efekt jak konwersja implicit:

W Przeciwnym Razie wyrażenie e może być jawnie przekonwertowane na typ T używając static_cast formularza static_cast<T>(e), jeżeli deklaracja {[9] } jest dobrze uformowana, dla jakiejś wymyślonej zmiennej tymczasowej t (8.5). Efekt takiego konwersja jawna jest taka sama jak wykonywanie deklaracji i inicjalizacji, a następnie za pomocą tymczasowego zmienna jako wynik konwersji. [...]

Dla odwrotności static_cast<> (od void* do T*), §5.2.9/13 Stany:

Wartość prvalue typu "wskaźnik do CV1 void" może być przekonwertowana na wartość prvalue typu "wskaźnik do cv2 T", gdzie T jest typem obiektu, a cv2 jest ta sama kwalifikacja cv, lub większa niż kwalifikacja CV1. [...] Wartość typu pointer to object converted to " wskaźnik do CV void" i z powrotem, ewentualnie z różnymi kwalifikacjami cv, musi mieć swoje oryginalna wartość.

Więc jeśli masz void* wskazujący na przechowywanie T obiektu (który jest wartością wskaźnika wynikającą z domyślnej konwersji T* na obiekt, to static_cast tego na T* wytworzy prawidłowy wskaźnik do obiektu.

Wracając do twojego pytania, poprzednie punkty sugerują, że jeśli mieć

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
void * pvt_ = &t_;

T* pT = new (&t_) T(args...);
void * pvT = pT;

Then

  • zapis *pT nakłada się dokładnie na pierwszy rozmiar(T) bajtów zapisu t_, tak że pvT == pvt_
  • pvt_ == static_cast<void*>(&t_)
  • static_cast<T*>(pvT) == pT
  • wzięte razem, że daje static_cast<T*>(static_cast<void*>(&t_)) == pT
 12
Author: JoergB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-31 19:41:25