Czy trywialne destruktory powodują aliasing
C ++ 11 §3.8.1 deklaruje, że dla obiektu z trywialnym destruktorem mogę zakończyć jego żywotność, przypisując do jego pamięci. Zastanawiam się, czy trywialne destruktory mogą przedłużyć żywotność obiektu i spowodować aliasing nieszczęść przez „zniszczenie obiektu”, który zakończył żywot znacznie wcześniej.
Na początek coś, co wiem, jest bezpieczne i wolne od aliasów
void* mem = malloc(sizeof(int));
int* asInt = (int*)mem;
*asInt = 1; // the object '1' is now alive, trivial constructor + assignment
short* asShort = (short*)mem;
*asShort = 2; // the object '1' ends its life, because I reassigned to its storage
// the object '2' is now alive, trivial constructor + assignment
free(mem); // the object '2' ends its life because its storage was released
Teraz coś, co nie jest tak jasne:
{
int asInt = 3; // the object '3' is now alive, trivial constructor + assignment
short* asShort = (short*)&asInt; // just creating a pointer
*asShort = 4; // the object '3' ends its life, because I reassigned to its storage
// the object '4' is now alive, trivial constructor + assignment
// implicitly, asInt->~int() gets called here, as a trivial destructor
} // 'the object '4' ends its life, because its storage was released
§6.7.2 stwierdza, że obiekty automatycznego przechowywania są niszczone na końcu zakresu, wskazując, że destruktor zostanie wywołany.Jeśli istnieje int do zniszczenia,*asShort = 2
jest naruszeniem aliasingu, ponieważ dereferencjonuję wskaźnik niepowiązanego typu. Ale jeśli długość życia liczby całkowitej skończyła się wcześniej*asShort = 2
, a następnie wywołuję int destruktor w krótkim czasie.
Widzę kilka konkurencyjnych sekcji dotyczących tego:
§ 3.8.8 czyta
Jeśli program kończy czas życia obiektu typu T ze statycznym (3.7.1), wątkiem (3.7.2) lub automatycznym (3.7.3) czasem przechowywania i jeśli T ma nietrywialny destruktor, 39 program musi upewnij się, że obiekt typu oryginalnego zajmuje to samo miejsce przechowywania, gdy ma miejsce niejawne wywołanie destruktora; w przeciwnym razie zachowanie programu jest niezdefiniowane.
Fakt, że wywołują typy T z nietrywialnym destruktorem, ponieważ dają niezdefiniowane zachowanie, wydaje mi się wskazywać, że posiadanie innego typu w tej lokalizacji pamięci z trywialnym destruktoremjest zdefiniowałem, ale nie mogłem znaleźć nigdzie w specyfikacji, która to zdefiniowała.
Taka definicja byłaby łatwa, gdyby zdefiniowano trywialny destruktor jako noop, ale w specyfikacji tej jest bardzo mało.
§6.7.3 wskazuje, że goto mogą przeskakiwać do zakresów, których zmienne mają trywialne konstruktory i trywialne destruktory. Wydaje się to sugerować wzorzec, w którym trywialne destruktory mogą być pomijane, ale wcześniejsza część specyfikacji dotycząca niszczenia obiektów na końcu zakresu nie wspomina o tym.
Wreszcie jest sassy czytanie:
§ 3.8.1 wskazuje, że wolno mi rozpocząć żywotność obiektu w dowolnym momencie, jeśli jego konstruktor jest trywialny. To wydaje się wskazywać, że mógłbym zrobić coś takiego
{
int asInt = 3;
short* asShort = (short*)&asInt;
*asShort = 4; // the object '4' is now alive, trivial constructor + assignment
// I declare that an object in the storage of &asInt of type int is
// created with an undefined value. Doing so reuses the space of
// the object '4', ending its life.
// implicitly, asInt->~int() gets called here, as a trivial destructor
}
Jedynym z tych odczytów, który wydaje się sugerować, że jakiekolwiek problemy z aliasingiem, jest sam §6.7.2. Wydaje się, że odczytany jako część całej specyfikacji trywialny destruktor nie powinien w żaden sposób wpływać na program (choć z różnych powodów). Czy ktoś wie, co dzieje się w tej sytuacji?