13

Il semble y avoir un accord que vous ne pouvez pas willy point de bon gré mal gré (un int *) dans un tableau char en raison des règles d'aliasing C++.Le placement est-il légalement requis pour placer un int dans un tableau char?

De cette autre question - Generic char[] based storage and avoiding strict-aliasing related UB - il semble qu'il est autorisé à (re) utiliser le stockage grâce à un placement nouveau.

alignas(int) char buf[sizeof(int)]; 

void f() { 
    // turn the memory into an int: (??) from the POV of the abstract machine! 
    ::new (buf) int; // is this strictly required? (aside: it's obviously a no-op) 

    // access storage: 
    *((int*)buf) = 42; // for this discussion, just assume the cast itself yields the correct pointer value 
} 

Alors, est le C++ juridique ci-dessus et est le placement nouveau réellement nécessaire pour la rendre légale?

+0

Connexe: http://stackoverflow.com/questions/38862092/is-it-legal-to-alias-a-char-array-through-a-pointer-to-int –

+0

https://godbolt.org/g/k2nVI9 –

+0

, duper potentiel très pertinent: https://stackoverflow.com/questions/40873520/reinterpret-cast-creating-a-trivially-default-constructible-object –

Répondre

12

Oui, l'emplacement new est nécessaire, sinon vous ne respecteriez pas l'aliasing strict (l'affectation est access).

est-ce qui précède juridique? Presque (bien que cela fonctionnera sur pratiquement toutes les implémentations). Le pointeur que vous avez créé via la distribution ne pointe pas vers l'objet, car le tableau (maintenant détruit) et l'objet int ne sont pas pointer-interconvertible; utilisez std::launder((int*)buf) ou, mieux encore, utilisez la valeur de retour de l'emplacement new.

+1

aliasing strict n'est pas violée par 'reinterpret_cast (BUF) = 42', car il accède au nouveau objet' int' comme un lvalue de type 'int'. Pointer-interconvertible est applicable lors de la conversion d'un pointeur "live" en un autre, pas lors de la conversion d'un pointeur "non-mort" en un objet "vivant". Edit: Je ne suis pas sûr si le «blanchiment» est nécessaire là-bas; les règles étaient encore en mouvement dernière je savais. – Potatoswatter

+1

@Potatoswatter Quel nouvel objet int? Je parlais de l'affaire dans laquelle il n'y avait aucun placement préalable nouveau pour créer un tel objet. Et je lis l'interconvertabilité du pointeur comme applicable en général lors de la réinterprétation des pointeurs. Quoiqu'il en soit, le pointeur créé par l'op utilisant la distribution ne pointe pas sur le nouvel objet tel que défini par P0137, donc «blanchir» devrait être nécessaire. – Columbo

+0

[basic.lval] interdit l'accès à un objet sauf par des valeurs de certains types. Il n'aborde pas la question de savoir si un objet existe encore. [basic.life]/6 en C++ 14 permet d'assigner 'reinterpret_cast' pour démarrer une vie entière; ceci est supprimé par P0137 donc C++ 17 fera (probablement) un placement-nouveau une exigence. Vous avez raison à propos de l'interconvertibilité du pointeur; P0137 le connecte à 'reinterpret_cast' via' static_cast' de 'void *'. Il est donc nécessaire de «blanchir» pour accéder à l'objet vivant (mais jamais de le «blanchir» pour le stocker dans un non-objet). – Potatoswatter

-3
*((int*)buf) = 42; 

écrit un int avec un int lvalue, donc il n'y a pas de problème de crénelage en premier lieu.