Namespaces
Variants

static_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Convertit entre les types en utilisant une combinaison de conversions implicites et définies par l'utilisateur.

Table des matières

Syntaxe

static_cast< type-cible  >( expression  )

Retourne une valeur de type target-type .

Explication

Seules les conversions suivantes peuvent être effectuées avec static_cast , sauf lorsque ces conversions supprimeraient la constance (ou la volatilité).

1) Si expression est une lvalue de type « cv1 Base » et target-type est « référence vers cv2 Derived », le résultat fait référence à l'objet de type Derived englobant expression si toutes les conditions suivantes sont satisfaites :
  • Derived est un type de classe complet.
  • Base est une classe de base de Derived .
  • cv1 n'est pas une qualification-cv plus forte que cv2 .
Si l'une des conditions suivantes est satisfaite, le programme est mal formé :
Si expression n'est pas réellement un sous-objet de classe de base d'un objet de type Derived , le comportement est indéfini.
struct B {};
struct D : B { B b; };
D d;
B& br1 = d;
B& br2 = d.b;
static_cast<D&>(br1); // OK, lvalue désignant l'objet original « d »
static_cast<D&>(br2); // UB : le sous-objet « b » n'est pas un sous-objet de classe de base
2) Si target-type est une « référence à rvalue vers Derived » et expression est une xvalue de type « (éventuellement qualifié cv) Base » telle que Base est une classe de base de Derived , le résultat et les contraintes d'une telle conversion sont les mêmes que ceux de la conversion « lvalue Base vers référence Derived ».
3) Si target-type est un type de référence à rvalue et que le type référencé est compatible par référence avec le type de expression , static_cast convertit la valeur de glvalue, prvalue de classe ou prvalue de tableau (jusqu'en C++17) toute lvalue (depuis C++17) expression en xvalue référençant le même objet que l'expression, ou sa sous-objet de classe de base (selon target-type ). [1]
Si target-type est une base inaccessible ou ambiguë du type de expression , le programme est mal formé.
Si expression est une lvalue de champ de bits , elle est d'abord convertie en prvalue du type sous-jacent.
(depuis C++11)
4) Si target-type est le type (éventuellement qualifié cv) void , la conversion n'a pas de résultat. Dans ce cas, expression est une expression à valeur ignorée .
5) Sinon, expression peut être explicitement convertie en target-type si

la déclaration target-type temp ( expression  ) ; est bien formée pour une variable temporaire inventée temp .

L'effet d'une telle conversion explicite est le même que d'effectuer la déclaration et l'initialisation puis d'utiliser temp comme résultat de la conversion. La expression  est utilisée comme une lvalue (jusqu'à C++11) une glvalue (depuis C++11) si et seulement si l'initialisation l'utilise comme une lvalue (jusqu'à C++11) une glvalue (depuis C++11) .

(jusqu'à C++17)

l'une des conditions suivantes est satisfaite :

  • Il existe une séquence de conversion implicite de expression vers target-type .
  • La résolution de surcharge pour une initialisation directe d'un objet ou référence de type target-type à partir de expression trouverait au moins une fonction viable.
  • target-type est un type agrégé ayant un premier élément x et il existe une séquence de conversion implicite de expression vers le type de x .
(depuis C++20)

La conversion explicite est définie comme suit :

  • Si target-type est un type référence, l'effet est le même que d'effectuer la déclaration et l'initialisation target-type temp ( expression  ) ; pour une variable temporaire inventée temp puis d'utiliser temp comme résultat de la conversion.
  • Sinon, l'objet résultat est initialisé directement à partir de expression .
(depuis C++17)
6) Sinon, si la conversion de expression vers target-type est l'inverse d'une séquence de conversion standard, et que la séquence de conversion ne contient aucune des conversions suivantes, la conversion peut être effectuée par static_cast :
(depuis C++17)
Si un programme utilise static_cast pour effectuer l'inverse d'une séquence de conversion standard mal formée, il est mal formé.
7) Sinon, les conversions lvalue-vers-rvalue, tableau-vers-pointeur et fonction-vers-pointeur sont appliquées à expression . Après ces conversions, seules les conversions suivantes peuvent être effectuées par static_cast :
a) Une valeur de type énumération délimitée peut être convertie en un type entier ou à virgule flottante.
  • Si target-type est (éventuellement qualifié cv) bool , le résultat est false si la valeur originale de expression est zéro et true pour toutes les autres valeurs.
  • Si target-type est un type intégral autre que (éventuellement qualifié cv) bool , la valeur reste inchangée si la valeur originale de expression peut être représentée par target-type . Sinon, la valeur résultante n'est pas spécifiée.
(jusqu'à C++20)
  • Si target-type est un type intégral, le résultat est le même que celui de la conversion vers le type sous-jacent de l'énumération puis vers target-type .
(depuis C++20)
  • Si target-type est un type à virgule flottante, le résultat est le même que celui de la conversion de la valeur originale vers target-type .
(depuis C++11)
b) Une valeur de type entier ou énumération peut être convertie en n'importe quel type d'énumération complet.
c) Une valeur d'un type à virgule flottante peut également être convertie vers n'importe quel type d'énumération complet. Le résultat est le même que convertir la valeur originale de expression d'abord vers le type sous-jacent de target-type , puis vers target-type lui-même.
d) Une valeur prvalue de type flottant peut être explicitement convertie vers tout autre type flottant.
  • Si la valeur source de expression peut être représentée exactement dans target-type , elle ne change pas.
  • Sinon, si la valeur source de expression est comprise entre deux valeurs représentables de target-type , le résultat de la conversion est un choix défini par l'implémentation de l'une ou l'autre de ces valeurs. [2]
  • Sinon, le comportement est indéfini.
(depuis C++23)
e) Une rvalue (jusqu'au C++11) Une prvalue (depuis C++11) de type « pointeur vers cv1 Base » peut être explicitement convertie en type « pointeur vers cv2 Derived » si toutes les conditions suivantes sont satisfaites :
  • Derived est un type de classe complet.
  • Base est une classe de base de Derived .
  • cv1 n'est pas une qualification cv plus forte que cv2 .
Si expression est une valeur de pointeur nul , le résultat est une valeur de pointeur nul de type target-type . Sinon, le résultat est un pointeur vers l'objet de type Derived contenant l'objet de type Base pointé par expression .
Si l'une des conditions suivantes est satisfaite, le programme est mal formé :
  • Base est une classe de base virtuelle de Derived .
  • Base est une classe de base d'une classe de base virtuelle de Derived .
  • Aucune conversion standard valide de « pointeur vers Derived » vers « pointeur vers Base » n'existe.
Si expression n'est pas une valeur de pointeur nul et ne pointe pas réellement vers un sous-objet de classe de base d'un objet de type Derived , le comportement est indéfini.
f) Une rvalue (jusqu'au C++11) Une prvalue (depuis C++11) de type « pointeur sur membre de Derived de type cv1 T » peut être explicitement convertie en type « pointeur sur membre de Base de type cv2 T » si toutes les conditions suivantes sont satisfaites :
  • Derived est un type classe complet.
  • Base est une classe de base de Derived .
  • cv1 n'est pas une qualification-cv plus forte que cv2 .
Si expression est une valeur de pointeur de membre nulle, le résultat est une valeur de pointeur de membre nulle de type target-type . Sinon, le résultat est un pointeur vers le membre original (éventuellement indirect) de la classe Base .
Si aucune conversion standard valide de « pointeur vers membre de Base de type T » vers « pointeur vers membre de Derived de type T » n'existe, le programme est mal formé.
Si expression n'est pas une valeur de pointeur de membre nulle et que le membre qu'elle désigne n'est pas un membre (éventuellement indirect) de la classe Base , le comportement est indéfini.
g) Une rvalue (jusqu'en C++11) Une prvalue (depuis C++11) de type « pointeur vers cv1 void » peut être explicitement convertie en type « pointeur vers cv2 T » si T est un type objet et cv1 n'est pas une qualification-cv plus forte que cv2 .
  • Si expression est une valeur de pointeur nul, le résultat est une valeur de pointeur nul du type target-type .
  • Si expression représente l'adresse A d'un octet en mémoire et A satisfait l'exigence d' alignement de T , alors la valeur de pointeur résultante représente également A .
  • Le résultat de toute autre conversion de pointeur de ce type est non spécifié.
  • Si expression est le résultat d'une conversion antérieure d'un objet de type « pointeur vers cv3 T », le résultat a la valeur originale.
(jusqu'en C++17)
  • Si expression représente l'adresse A d'un octet en mémoire mais A ne satisfait pas l'exigence d' alignement de T , alors la valeur de pointeur résultante est non spécifiée.
  • Sinon, si expression pointe vers un objet a , et qu'il existe un objet b de type T (en ignorant la qualification-cv) qui est interconvertible par pointeur (voir ci-dessous) avec a , le résultat est un pointeur vers b .
  • Sinon, la valeur du pointeur n'est pas modifiée par la conversion.
(depuis C++17)

Comme pour toutes les expressions de cast, le résultat est :

  • un lvalue si target-type est un type référence lvalue ou une référence rvalue vers un type fonction (depuis C++11) ;
  • une xvalue si target-type est une référence à valeur droite vers un type objet ;
(depuis C++11)
  • un prvalue sinon.
  1. Ce type de static_cast est utilisé pour implémenter la sémantique de déplacement dans std::move .
  2. Si l'arithmétique IEEE est supportée, l'arrondi par défaut est vers la valeur la plus proche.

Objets convertibles par pointeur

Deux objets a et b sont interchangeables par pointeur si :

  • ils sont le même objet, ou
  • l'un est un objet union et l'autre est un membre de données non statique de cet objet, ou
  • l'un est un standard-layout class object et l'autre est le premier membre de données non statique de cet objet ou de tout sous-objet de classe de base de cet objet, ou
  • il existe un objet c tel que a et c sont pointer-interconvertible, et c et b sont pointer-interconvertible.
union U { int a; double b; } u;
void* x = &u;                        // la valeur de x est « pointeur vers u »
double* y = static_cast<double*>(x); // la valeur de y est « pointeur vers u.b »
char* z = static_cast<char*>(x);     // la valeur de z est « pointeur vers u »

Notes

Les conversions de base-vers-dérivé ( downcasts ) utilisant static_cast n'effectuent aucune vérification à l'exécution pour garantir que le type dynamique de l'objet pointé/référencé est Derived , et ne peuvent être utilisées en sécurité que si ce prérequis est garanti par d'autres moyens, comme lors de l'implémentation du polymorphisme statique . Un downcast sécurisé peut être effectué avec dynamic_cast .

static_cast peut également être utilisé pour lever l'ambiguïté des surcharges de fonctions en effectuant une conversion fonction-vers-pointeur vers un type spécifique, comme dans

std::for_each(files.begin(), files.end(),
              static_cast<std::ostream&(*)(std::ostream&)>(std::flush));

Mots-clés

static_cast

Exemple

#include <iostream>
#include <vector>
struct B
{
    int m = 42;
    const char* hello() const
    {
        return "Hello world, this is B!\n";
    }
};
struct D : B
{
    const char* hello() const
    {
        return "Hello world, this is D!\n";
    }
};
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
int main()
{
    // 1. conversion descendante statique
    D d;
    B& br = d; // conversion ascendante via conversion implicite
    std::cout << "1) " << br.hello();
    D& another_d = static_cast<D&>(br); // conversion descendante
    std::cout << "1) " << another_d.hello();
    // 3. lvalue vers xvalue
    std::vector<int> v0{1, 2, 3};
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v0);
    std::cout << "3) après déplacement, v0.size() = " << v0.size() << '\n';
    // 4. expression à valeur ignorée
    static_cast<void>(v2.size());
    // 5. conversion d'initialisation
    int n = static_cast<int>(3.14);
    std::cout << "5) n = " << n << '\n';
    std::vector<int> v = static_cast<std::vector<int>>(10);
    std::cout << "5) v.size() = " << v.size() << '\n';
    // 6. inverse de la conversion implicite
    void* nv = &n;
    int* ni = static_cast<int*>(nv);
    std::cout << "6) *ni = " << *ni << '\n';
    // 7a. enum délimité vers int
    E e = E::TWO;
    int two = static_cast<int>(e);
    std::cout << "7a) " << two << '\n';
    // 7b. int vers enum, enum vers un autre enum
    E e2 = static_cast<E>(two);
    [[maybe_unused]]
    EU eu = static_cast<EU>(e2);
    // 7f. pointeur vers membre en conversion ascendante
    int D::*pm = &D::m;
    std::cout << "7f) " << br.*static_cast<int B::*>(pm) << '\n';
    // 7g. void* vers n'importe quel pointeur d'objet
    void* voidp = &e;
    [[maybe_unused]]
    std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}

Sortie :

1) Hello world, this is B!
1) Hello world, this is D!
3) after move, v0.size() = 0
5) n = 3
5) v.size() = 10
6) *ni = 3
7a) 2
7f) 42
**Traduction :**
1) Bonjour le monde, voici B !
1) Bonjour le monde, voici D !
3) après déplacement, v0.size() = 0
5) n = 3
5) v.size() = 10
6) *ni = 3
7a) 2
7f) 42
**Notes de traduction :** - Les balises HTML et attributs sont conservés intacts - Le contenu dans `
` n'est pas traduit (comme demandé)
- Les termes C++ (`size()`, `move`, `n`, `v`, `ni`) sont conservés
- Seul le texte anglais ordinaire a été traduit en français

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 Appliqué à Comportement publié Comportement corrigé
CWG 137 C++98 la constance et la volatilité des
pointeurs vers void pouvaient être supprimées par cast
les qualifications cv ne peuvent pas
être supprimées par cast dans ces cas
CWG 427 C++98 la conversion descendante pouvait être ambiguë avec l'initialisation directe sélectionne la conversion descendante dans ce cas
CWG 439 C++98 lors de la conversion d'un "pointeur vers objet" en "pointeur vers
void " puis de retour vers lui-même, il ne pouvait préserver sa
valeur que si le type résultat avait la même qualification cv
la qualification cv
peut être différente
CWG 1094 C++98 la conversion des valeurs à virgule flottante
vers les valeurs d'énumération n'était pas spécifiée
spécifiée
CWG 1320 C++11 la conversion des valeurs d'énumération scopée
vers bool n'était pas spécifiée
spécifiée
CWG 1412 C++98 le résultat de la conversion de
"pointeur vers
void " vers "pointeur vers objet" n'était pas clair
clarifié
CWG 1447 C++11 la conversion des champs de bits vers les références rvalue
n'était pas spécifiée (impossible de lier des références aux champs de bits)
spécifiée
CWG 1766 C++98 la conversion des valeurs entières ou d'énumération vers les valeurs d'énumération
produisait un résultat non spécifié si expression était hors limites
le comportement est
indéfini dans ce cas
CWG 1832 C++98 la conversion des valeurs entières ou d'énumération vers
les valeurs d'énumération autorisait target-type à être incomplet
non autorisé
CWG 2224 C++98 la conversion d'un membre de type classe de base vers
son objet complet de type classe dérivée était valide
le comportement est
indéfini dans ce cas
CWG 2254 C++11 un objet de classe standard-layout sans membres de données
était interconvertible par pointeur avec sa première classe de base
il est interconvertible par pointeur
avec n'importe laquelle de ses classes de base
CWG 2284 C++11 un objet union non standard-layout et un membre de données non statique
de cet objet n'étaient pas interconvertibles par pointeur
ils le sont
CWG 2310 C++98 pour les conversions de pointeur base-vers-dérivé et
les conversions de pointeur-vers-membre dérivé-vers-base,
le type classe dérivée pouvait être incomplet
doit être complet
CWG 2338 C++11 la conversion vers les types d'énumération avec type sous-jacent fixe
résultait en un comportement indéfini si expression était hors limites
convertit d'abord vers le type sous-jacent
(pas de comportement indéfini)
CWG 2499 C++11 une classe standard-layout pouvait avoir une classe de base non interconvertible
par pointeur, même si tous les sous-objets de base ont la même adresse
elle n'en a pas
CWG 2718 C++98 pour les conversions de référence base-vers-dérivé,
le type classe dérivée pouvait être incomplet
doit être complet
CWG 2882 C++98 il n'était pas clair si static_cast < void > ( expr ) tente
de former une séquence de conversion implicite de expr vers void
aucune tentative dans ce cas

Références

  • Norme C++23 (ISO/IEC 14882:2024) :
  • 7.6.1.9 Conversion statique [expr.static.cast]
  • Norme C++20 (ISO/CEI 14882:2020) :
  • 7.6.1.8 Conversion statique [expr.static.cast]
  • Norme C++17 (ISO/IEC 14882:2017) :
  • 8.2.9 Conversion statique [expr.static.cast]
  • Norme C++14 (ISO/CEI 14882:2014) :
  • 5.2.9 Conversion statique [expr.static.cast]
  • Norme C++11 (ISO/IEC 14882:2011) :
  • 5.2.9 Conversion statique [expr.static.cast]
  • Norme C++98 (ISO/CEI 14882:1998) :
  • 5.2.9 Conversion statique [expr.static.cast]
  • Norme C++03 (ISO/IEC 14882:2003) :
  • 5.2.9 Conversion statique [expr.static.cast]

Voir aussi