reinterpret_cast crea un object costruibile di default banalmente

cppreference afferma che:

Oggetti con banali costruttori predefiniti possono essere creati usando reinterpret_cast su qualsiasi memoria opportunamente allineata, ad esempio sulla memoria allocata con std::malloc .

Ciò implica che quanto segue è un codice ben definito:

 struct X { int x; }; alignas(X) char buffer[sizeof(X)]; // (A) reinterpret_cast(buffer)->x = 42; // (B) 

Seguono tre domande:

  1. Questa citazione è corretta?
  2. Se sì, a che punto inizia la vita della X ? Se in linea (B) , è il cast stesso che si considera l’acquisizione dello spazio di archiviazione? Se sulla linea (A) , e se ci fosse un ramo tra (A) e (B) che costruisca condizionalmente una X o qualche altro pod, Y ?
  3. A questo proposito, qualcosa cambia tra C ++ 11 e C ++ 1z?

Si noti che questo è un vecchio collegamento. La formulazione è stata modificata in risposta a questa domanda. Ora si legge:

    A differenza di C, tuttavia, gli oggetti con costruttori di base banali non possono essere creati semplicemente reinterpretando lo storage adeguatamente allineato, come la memoria allocata con std::malloc : placement-new è richiesto per introdurre formalmente un nuovo object ed evitare potenziali comportamenti indefiniti.

    Non esiste alcun object X , vivente o no, così facendo finta che ci sia un risultato in un comportamento indefinito.

    [intro.object] / 1 esplora in modo esaustivo quando vengono creati gli oggetti:

    Un object è creato da una definizione ([basic.def]), da una nuova espressione ([expr.new]), quando cambia implicitamente il membro attivo di un’unione ([class.union]), o quando un object temporaneo viene creato ([conv.rval], [class.temporary]).

    Con l’adozione di P0137R1 , questo paragrafo è la definizione del termine “object”.

    Esiste una definizione di un object X ? No. Esiste una nuova espressione ? No. C’è un sindacato? No. Esiste un costrutto linguistico nel codice che crea un object X temporaneo? No.

    Qualsiasi cosa [la vita di base] dica sulla durata di un object con un’inizializzazione vacua è irrilevante. Perché ciò si applichi, devi avere un object in primo luogo. Non lo fai.

    C ++ 11 ha approssimativamente lo stesso paragrafo, ma non lo usa come definizione di “object”. Tuttavia, l’interpretazione è la stessa. L’interpretazione alternativa – il trattamento di [basic.life] come creazione di un object non appena viene acquisita una memoria adeguata – significa che stai creando gli oggetti di Schrödinger * , che contraddice N3337 [intro.object] / 6 :

    Due oggetti che non sono campi di bit possono avere lo stesso indirizzo se uno è un sub-object dell’altro, o se almeno uno è un sottoobject di class base di dimensione zero e sono di tipi diversi; altrimenti, avranno indirizzi distinti.


    * Lo stoccaggio con l’allineamento e le dimensioni corretti per un tipo T è per definizione un magazzino con l’allineamento e le dimensioni corretti per ogni altro tipo le cui dimensioni e requisiti di allineamento sono uguali o inferiori a quelli di T Pertanto, tale interpretazione implica che l’ottenimento della memorizzazione crea contemporaneamente un insieme infinito di oggetti con tipi diversi in detta memoria, tutti aventi lo stesso indirizzo.

    Questa analisi è basata su n4567 e utilizza i numeri di sezione da esso.

    §5.2.10 / 7: Quando un valore di prvalore v di tipo puntatore object viene convertito nel puntatore object “puntatore a cv T”, il risultato è static_cast(static_cast(v)) .

    Quindi, in questo caso, reinterpret_cast(buffer) è lo stesso di static_cast(static_cast(buffer)) . Questo ci porta a guardare le parti rilevanti su static_cast :

    §5.2.9 / 13: Un valore preregolato di tipo “puntatore a cv1 void” può essere convertito in un valore di tipo “puntatore a cv2 T”, dove T è un tipo di object e cv2 è la stessa qualifica di cv come, o maggiore qualifica cv che, cv1 . Il valore del puntatore nullo viene convertito nel valore del puntatore nullo del tipo di destinazione. Se il valore del puntatore originale rappresenta l’indirizzo A di un byte in memoria e A soddisfa il requisito di allineamento di T , il valore del puntatore risultante rappresenta lo stesso indirizzo del valore del puntatore originale, ovvero A

    Credo sia sufficiente dire che la citazione originale è corretta: questa conversione dà risultati definiti.

    Per quanto riguarda la durata, dipende da che vita stai parlando. Il cast crea un nuovo object di tipo puntatore – un temporaneo, che ha una durata a partire dalla linea in cui si trova il cast, e termina quando esce dall’ambito. Se hai due diverse conversioni che avvengono in modo condizionale, ogni puntatore ha una durata che parte dalla posizione del cast che lo ha creato.

    Nessuna di queste influisce sulla durata dell’object che fornisce la memoria sottostante, che è ancora un buffer , e ha esattamente la stessa durata, indipendentemente dal fatto che si crei o meno un puntatore (dello stesso tipo o convertito) in tale memoria.