launder
| (depuis C++17) <new>
|
||
template< class T >
constexpr T* launder( T* p ) noexcept;
|
Barrière de dévirtualisation par rapport à | |
. Retourne un pointeur vers un objet à la même adresse que celle représentée par p, 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 p d'origine.
*pFormellement, étant donné
le pointeur
- représente l'adresse
pd'un octet en mémoireAun objet - est situé à l'adresse
xest dans saA xdurée de viele type de- est le même que
x, en ignorant les qualificateurs cv à chaque niveauTtout octet qui serait accessible via le résultat est accessible via p (les octets sont accessibles via un pointeur qui pointe vers un objet - si ces octets se trouvent dans le stockage d'un objet
yqui estzinterconvertible par pointeur avec , ou dans le tableau immédiatement englobant dontyest un élément).zAlors
retourne une valeur de type std::launder(p) qui pointe vers l'objet T*. Sinon, le comportement est indéfini.
xLe programme est mal formé si
est un type fonction ou T (éventuellement qualifié cv).
void peut être utilisé dans une
std::launderexpression constante de base si et seulement si la valeur (convertie) de son argument peut être utilisée à la place de l'invocation de la fonction. En d'autres termes, n'atténue pas les restrictions dans l'évaluation constante.
std::launderSujet à des restrictions sur l'échange
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 |