std:: launder
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Functions | ||||
|
(C++11)
|
||||
| Classes | ||||
|
(C++11)
|
||||
|
(C++17)
|
||||
| Types | ||||
| Objects | ||||
|
(C++20)
|
||||
| Object access | ||||
|
launder
(C++17)
|
|
Défini dans l'en-tête
<new>
|
||
|
template
<
class
T
>
constexpr T * launder ( T * p ) noexcept ; |
(depuis C++17) | |
Barrière de dévirtualisation par rapport à p . Retourne un pointeur vers un objet à la même adresse que p représente, tandis que l'objet peut être un nouveau sous-objet de classe de base dont la classe la plus dérivée est différente de celle de l'objet original * p .
Formellement, étant donné
-
le pointeur
p
représente l'adresse
Ad'un octet en mémoire -
un objet
x
est situé à l'adresse
A - x est dans sa durée de vie
-
le type de
x
est identique à
T, en ignorant les qualificatifs cv à chaque niveau - chaque octet qui serait accessible via le résultat est accessible via p (les octets sont accessibles via un pointeur qui pointe vers un objet y si ces octets se trouvent dans le stockage d'un objet z qui est pointeur-interconvertible avec y , ou dans le tableau immédiatement englobant dont z est un élément).
Alors
std
::
launder
(
p
)
retourne une valeur de type
T*
qui pointe vers l'objet
x
. Sinon, le comportement est indéfini.
Le programme est mal formé si
T
est un type fonction ou (éventuellement qualifié cv)
void
.
std::launder
peut être utilisé dans une
expression constante de base
si et seulement si la valeur (convertie) de son argument peut être utilisée à la place de l'invocation de fonction. En d'autres termes,
std::launder
ne relâche pas les restrictions lors de l'évaluation constante.
Notes
std::launder
n'a aucun effet sur son argument. Sa valeur de retour doit être utilisée pour accéder à l'objet. Ainsi, il est toujours une erreur d'ignorer la valeur de retour.
Utilisations typiques de
std::launder
incluent :
- Obtention d'un pointeur vers un objet créé dans le stockage d'un objet existant du même type, où les pointeurs vers l'ancien objet ne peuvent pas être réutilisés (par exemple, parce que l'un ou l'autre objet est un sous-objet de classe de base) ;
-
Obtention d'un pointeur vers un objet créé par un placement
newà partir d'un pointeur vers un objet fournissant le stockage pour cet objet.
La restriction de
reachability
garantit que
std::launder
ne peut pas être utilisé pour accéder aux octets non accessibles via le pointeur original, interférant ainsi avec l'analyse d'échappement du compilateur.
int x[10]; auto p = std::launder(reinterpret_cast<int(*)[10]>(&x[0])); // OK int x2[2][10]; auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0])); // Comportement indéfini : x2[1] serait accessible via le pointeur résultant vers x2[0] // mais n'est pas accessible depuis la source struct X { int a[10]; } x3, x4[2]; // disposition standard ; supposons aucun remplissage auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0])); // Comportement indéfini : x4[1] serait accessible via le pointeur résultant vers x4[0].a // (qui est interconvertible par pointeur avec x4[0]) mais n'est pas accessible depuis la source struct Y { int a[10]; double y; } x5; auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0])); // Comportement indéfini : x5.y serait accessible via le pointeur résultant vers x5.a // mais n'est pas accessible depuis la source
Exemple
#include <cassert> #include <cstddef> #include <new> struct Base { virtual int transmogrify(); }; struct Derived : Base { int transmogrify() override { new(this) Base; return 2; } }; int Base::transmogrify() { new(this) Derived; return 1; } static_assert(sizeof(Derived) == sizeof(Base)); int main() { // Cas 1 : le nouvel objet n'a pas pu être remplacé de manière transparente car // il s'agit d'un sous-objet de base mais l'ancien objet est un objet complet. Base base; int n = base.transmogrify(); // int m = base.transmogrify(); // comportement indéfini int m = std::launder(&base)->transmogrify(); // OK assert(m + n == 3); // Cas 2 : accès à un nouvel objet dont le stockage est fourni // par un tableau d'octets via un pointeur vers le tableau. struct Y { int z; }; alignas(Y) std::byte s[sizeof(Y)]; Y* q = new(&s) Y{2}; const int f = reinterpret_cast<Y*>(&s)->z; // L'accès au membre de classe est un // comportement indéfini : reinterpret_cast<Y*>(&s) // a la valeur "pointeur vers s" et ne // pointe pas vers un objet Y const int g = q->z; // OK const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK [](...){}(f, g, h); // produit l'effet [[maybe_unused]] }
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Applicable à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| LWG 2859 | C++17 |
la définition de
reachable
ne prenait pas en compte l'arithmétique
des pointeurs depuis un objet pointer-interconvertible |
inclus |
| LWG 3495 | C++17 |
std::launder
pourrait rendre un pointeur vers un membre
inactif déréférençable dans une expression constante |
interdit |