Namespaces
Variants

Explicit type 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 explicites et implicites.

Table des matières

Syntaxe

( type-id ) unary-expression (1)
simple-type-specifier ( expression-list  (optionnel) )
simple-type-specifier ( initializer-list  (optionnel) )
(2) (jusqu'en C++11)
(depuis C++11)
simple-type-specifier { initializer-list  (optionnel) } (3) (depuis C++11)
simple-type-specifier { designated-initializer-list } (4) (depuis C++20)
typename identifier ( initializer-list  (optionnel) ) (5) (depuis C++11)
typename identifier { initializer-list  (optionnel) } (6) (depuis C++11)
typename identifier { designated-initializer-list } (7) (depuis C++20)

Convertit explicitement n'importe quel nombre de valeurs en une valeur du type cible.

1) Conversion de type explicite (notation de cast), également appelée cast de style C .
2-7) Conversion de type explicite (notation fonctionnelle), également appelée cast de style fonction .
type-id - un identifiant de type
unary-expression - une expression unaire (dont l'opérateur principal n'a pas une précédence supérieure à celle du cast de style C)
simple-type-specifier - un spécificateur de type simple
expression-list - une liste d'expressions séparées par des virgules (à l'exception des expressions virgules non parenthésées)
initializer-list - une liste de clauses d'initialisation séparées par des virgules
designated-initializer-list - une liste de clauses d'initialisation désignées séparées par des virgules
identifier - un identifiant (éventuellement qualifié) (incluant les identifiants de modèle )

Explication

1) Lorsque le cast de style C est rencontré, le compilateur tente de l'interpréter comme les expressions de cast suivantes, dans cet ordre :
a) const_cast < type-id  > ( unary-expression  ) ;
b) static_cast < type-id  > ( unary-expression  ) , avec extensions : un pointeur ou une référence vers une classe dérivée est également autorisé à être converti en pointeur ou référence vers une classe de base non ambiguë (et vice versa) même si la classe de base est inaccessible (c'est-à-dire que cette conversion ignore le spécificateur d'héritage privé). La même règle s'applique à la conversion d'un pointeur vers membre en pointeur vers membre d'une classe de base non virtuelle non ambiguë ;
c) a static_cast (avec extensions) suivi de const_cast ;
d) reinterpret_cast < type-id  > ( unary-expression  ) ;
e) a reinterpret_cast suivi de const_cast .
Le premier choix qui satisfait aux exigences de l'opérateur de cast respectif est sélectionné, même s'il est mal formé (voir l'exemple). Si un static_cast suivi d'un const_cast est utilisé et que la conversion peut être interprétée de plusieurs manières, la conversion est mal formée.
De plus, les conversions de style C peuvent convertir depuis, vers et entre des pointeurs vers des types de classe incomplets. Si type-id et le type de unary-expression sont tous deux des pointeurs vers des types de classe incomplets, il n'est pas spécifié si static_cast ou reinterpret_cast est sélectionné.
2-7) Une conversion de style fonction spécifie un type ( simple-type-specifier  ou identifier  (depuis C++11) ) et un initialiseur (les parties restantes), elle construit une valeur du type cible T , qui est déterminé à partir du type spécifié et de l'initialiseur (depuis C++17) :

T est le type spécifié.

(jusqu'à C++17)

T est déterminé comme suit :

(depuis C++23)
  • Sinon, T est le type spécifié.
(depuis C++17)
Le résultat de la conversion est déterminé comme suit :
  • Si la conversion de type fonction est de syntaxe (2) , et qu'il y a exactement une expression entre parenthèses, cette conversion est équivalente à la conversion de style C correspondante.
  • Sinon, si T est (éventuellement qualifié cv) void , le résultat est une rvalue (jusqu'en C++11) une prvalue (depuis C++11) de type void qui n'effectue aucune initialisation.
  • Si l'initialiseur n'est pas ( ) , le programme est mal formé.
(jusqu'en C++11)
  • Si l'initialiseur n'est pas ( ) ou { } après l'expansion de paquet (le cas échéant), le programme est mal formé.
(depuis C++11)
  • Sinon, si T est un type référence, la conversion de type fonction a le même effet que l'initialisation directe d'une variable inventée t de type T à partir de l'initialiseur spécifié, et le résultat est le t initialisé.
  • Le résultat est une lvalue.
(jusqu'en C++11)
  • Si T est un type référence lvalue ou une référence rvalue vers un type fonction, le résultat est une lvalue.
  • Sinon, le résultat est une xvalue.
(depuis C++11)
  • Sinon, le résultat est une rvalue (jusqu'en C++11) une prvalue (depuis C++11) de type T désignant un temporaire (jusqu'en C++17) dont l'objet résultat est (depuis C++17) initialisé directement avec l'initialiseur spécifié.

Résolution d'Ambiguïté

Instruction de déclaration ambiguë

En cas d'ambiguïté entre une instruction d'expression avec une expression de conversion de style fonction comme sous-expression la plus à gauche et une instruction de déclaration, l'ambiguïté est résolue en la traitant comme une déclaration. Cette désambiguïsation est purement syntaxique : elle ne prend pas en compte la signification des noms apparaissant dans l'instruction, sauf pour déterminer s'ils sont des noms de type :

struct M {};
struct L { L(M&); };
M n;
void f()
{
    M(m);    // déclaration, équivalent à M m;
    L(n);    // déclaration mal formée, équivalent à L n;
    L(l)(m); // toujours une déclaration, équivalent à L l((m));
}

Cependant, si le déclarateur le plus externe dans l'instruction de déclaration ambiguë possède un type de retour trailing , l'instruction ne sera traitée comme une instruction de déclaration que si le type de retour trailing commence par auto :

struct M;
struct S
{
    S* operator()();
    int N;
    int M;
    void mem(S s)
    {
        auto(s)()->M; // expression (S::M cache ::M), invalide avant C++23
    }
};
void f(S s)
{
    {
        auto(s)()->N; // expression, invalide avant C++23
        auto(s)()->M; // déclaration de fonction, équivalent à M s();
    }
    {
        S(s)()->N;    // expression
        S(s)()->M;    // expression
    }
}
(depuis C++11)

Paramètre de fonction ambigu

L'ambiguïté ci-dessus peut également se produire dans le contexte d'une déclaration. Dans ce contexte, le choix se situe entre une déclaration d'objet avec un cast de style fonction comme initialiseur et une déclaration impliquant un déclarateur de fonction avec un ensemble redondant de parenthèses autour d'un nom de paramètre. La résolution consiste également à considérer toute construction, telle que la déclaration de paramètre potentielle, qui pourrait possiblement être une déclaration comme étant une déclaration :

struct S
{
    S(int);
};
void foo(double a)
{
    S w(int(a)); // déclaration de fonction : possède un paramètre `a` de type int
    S x(int());  // déclaration de fonction : possède un paramètre sans nom de type int(*)() 
                 // qui est ajusté à partir de int()
    // Méthodes pour éviter l'ambiguïté :
    S y((int(a))); // déclaration d'objet : parenthèses supplémentaires
    S y((int)a);   // déclaration d'objet : cast de style C
    S z = int(a);  // déclaration d'objet : aucune ambiguïté pour cette syntaxe
}

Cependant, si le déclarateur le plus externe dans la déclaration de paramètre ambiguë possède un type de retour en suffixe , l'ambiguïté ne sera résolue en le traitant comme une déclaration que s'il commence par auto :

typedef struct BB { int C[2]; } *B, C;
void foo()
{
    S a(B()->C);    // déclaration d'objet : B()->C ne peut pas déclarer un paramètre
    S b(auto()->C); // déclaration de fonction : possède un paramètre sans nom de type C(*)()
                    // qui est ajusté à partir de C()
}
(depuis C++11)

Identifiant de type ambigu

Une ambiguïté peut survenir de la similarité entre un cast de style fonction et un type-id . La résolution est que toute construction qui pourrait possiblement être un type-id dans son contexte syntaxique doit être considérée comme un type-id :

// `int()` et `int(unsigned(a))` peuvent tous deux être analysés comme type-id :
// `int()`            représente une fonction retournant int
//                    et ne prenant aucun argument
// `int(unsigned(a))` représente une fonction retournant int
//                    et prenant un argument de type unsigned
void foo(signed char a)
{
    sizeof(int());            // type-id (incorrect)
    sizeof(int(a));           // expression
    sizeof(int(unsigned(a))); // type-id (incorrect)
    (int()) + 1;            // type-id (incorrect)
    (int(a)) + 1;           // expression
    (int(unsigned(a))) + 1; // type-id (incorrect)
}

Cependant, si le abstract-declarator le plus externe dans le type-id ambigu possède un trailing return type , l'ambiguïté ne sera résolue en le traitant comme un type-id que s'il commence par auto :

typedef struct BB { int C[2]; } *B, C;
void foo()
{
    sizeof(B()->C[1]);    // OK, sizeof(expression)
    sizeof(auto()->C[1]); // error: sizeof of a function returning an array
}
(depuis C++11)

Notes

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_auto_cast 202110L (C++23) auto ( x ) et auto { x }

Exemple

#include <cassert>
#include <iostream>
double f = 3.14;
unsigned int n1 = (unsigned int)f; // C-style cast
unsigned int n2 = unsigned(f);     // function-style cast
class C1;
class C2;
C2* foo(C1* p)
{
    return (C2*)p; // casts incomplete type to incomplete type
}
void cpp23_decay_copy_demo()
{
    auto inc_print = [](int& x, const int& y)
    {
        ++x;
        std::cout << "x:" << x << ", y:" << y << '\n';
    };
    int p{1};
    inc_print(p, p); // prints x:2 y:2, because param y here is an alias of p
    int q{1};
    inc_print(q, auto{q}); // prints x:2 y:1, auto{q} (C++23) casts to prvalue,
                           // so the param y is a copy of q (not an alias of q)
}
// In this example, C-style cast is interpreted as static_cast
// even though it would work as reinterpret_cast
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
int main()
{
    D* d = nullptr;
//  A* a = (A*)d;                   // compile-time error
    A* a = reinterpret_cast<A*>(d); // this compiles
    assert(a == nullptr);
    cpp23_decay_copy_demo();
}

Sortie :

x:2 y:2
x:2 y:1

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 1223
( P2915R0 )
C++11 l'ajout du type de retour en suffixe introduisait plus d'ambiguïtés les résout
CWG 1893 C++11 le cast de style fonction ne prenait pas en compte les expansions de pack les prend en compte
CWG 2351 C++11 void { } était mal formé rendu bien formé
CWG 2620 C++98 la résolution des paramètres de fonction ambigus
pouvait être mal interprétée
amélioration de la formulation
CWG 2828 C++98 un cast de style C était mal formé si plusieurs interprétations
d'un static_cast suivi d'un const_cast existaient,
indépendamment de l'utilisation réelle de ces conversions
ne considère que les
conversions
potentiellement utilisées
CWG 2894 C++98 les casts de style fonction pouvaient créer des rvalues de référence ne peuvent créer que des lvalues de référence

Références

  • Norme C++23 (ISO/CEI 14882:2024) :
  • 7.6.1.4 Conversion explicite de type (notation fonctionnelle) [expr.type.conv]
  • 7.6.3 Conversion explicite de type (notation de cast) [expr.cast]
  • Norme C++20 (ISO/CEI 14882:2020) :
  • 7.6.1.4 Conversion explicite de type (notation fonctionnelle) [expr.type.conv]
  • 7.6.3 Conversion explicite de type (notation de cast) [expr.cast]
  • Norme C++17 (ISO/CEI 14882:2017) :
  • 8.2.3 Conversion de type explicite (notation fonctionnelle) [expr.type.conv]
  • 8.4 Conversion de type explicite (notation de cast) [expr.cast]
  • Norme C++14 (ISO/CEI 14882:2014) :
  • 5.2.3 Conversion de type explicite (notation fonctionnelle) [expr.type.conv]
  • 5.4 Conversion de type explicite (notation de cast) [expr.cast]
  • Norme C++11 (ISO/CEI 14882:2011) :
  • 5.2.3 Conversion de type explicite (notation fonctionnelle) [expr.type.conv]
  • 5.4 Conversion de type explicite (notation de cast) [expr.cast]
  • Norme C++03 (ISO/CEI 14882:2003) :
  • 5.2.3 Conversion explicite de type (notation fonctionnelle) [expr.type.conv]
  • 5.4 Conversion explicite de type (notation de cast) [expr.cast]
  • Norme C++98 (ISO/CEI 14882:1998) :
  • 5.2.3 Conversion explicite de type (notation fonctionnelle) [expr.type.conv]
  • 5.4 Conversion explicite de type (notation de cast) [expr.cast]

Voir aussi

const_cast conversion ajoute ou supprime const
static_cast conversion effectue des conversions basiques
dynamic_cast conversion effectue des conversions polymorphes vérifiées
reinterpret_cast conversion effectue des conversions générales de bas niveau
conversions standard conversions implicites d'un type à un autre
documentation C pour opérateur de cast