Namespaces
Variants

std:: launder

From cppreference.net
Utilities library
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
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 A d'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