User-defined conversion function
Permet la conversion implicite ou la conversion explicite d'un type classe vers un autre type.
Table des matières |
Syntaxe
La fonction de conversion est déclarée comme une fonction membre non statique ou un modèle de fonction membre sans paramètres, sans type de retour explicite, et avec un nom de la forme :
operator
conversion-type-id
|
(1) | ||||||||
explicit
operator
conversion-type-id
|
(2) | (depuis C++11) | |||||||
explicit (
expression
)
operator
conversion-type-id
|
(3) | (depuis C++20) | |||||||
conversion-type-id
est un
type-id
sauf que les opérateurs de fonction et de tableau
[]
ou
()
ne sont pas autorisés dans son déclarateur (ainsi la conversion vers des types tels que pointeur vers tableau nécessite un alias de type/typedef ou un template d'identité : voir ci-dessous). Indépendamment du typedef,
conversion-type-id
ne peut pas représenter un type tableau ou un type fonction.
Bien que le type de retour ne soit pas autorisé dans la déclaration d'une fonction de conversion définie par l'utilisateur, la
decl-specifier-seq
de
la grammaire de déclaration
peut être présente et peut inclure tout spécificateur autre que
type-specifier
ou le mot-clé
static
. En particulier, outre
explicit
, les spécificateurs
inline
,
virtual
,
constexpr
(depuis C++11)
,
consteval
(depuis C++20)
, et
friend
sont également autorisés (notez que
friend
nécessite un nom qualifié :
friend
A
::
operator
B
(
)
;
).
Lorsqu'une telle fonction membre est déclarée dans la classe X, elle effectue la conversion de X vers conversion-type-id :
struct X { // conversion implicite operator int() const { return 7; } // conversion explicite explicit operator int*() const { return nullptr; } // Erreur : opérateur de tableau non autorisé dans l'identifiant de type de conversion // operator int(*)[3]() const { return nullptr; } using arr_t = int[3]; operator arr_t*() const { return nullptr; } // OK si effectué via typedef // operator arr_t () const; // Erreur : conversion vers tableau non autorisée dans tous les cas }; int main() { X x; int n = static_cast<int>(x); // OK : définit n à 7 int m = x; // OK : définit m à 7 int* p = static_cast<int*>(x); // OK : définit p à null // int* q = x; // Erreur : aucune conversion implicite int (*pa)[3] = x; // OK }
Explication
La fonction de conversion définie par l'utilisateur est invoquée dans la deuxième étape de la conversion implicite , qui consiste en zéro ou un constructeur de conversion ou zéro ou une fonction de conversion définie par l'utilisateur.
Si les fonctions de conversion et les constructeurs de conversion peuvent tous deux être utilisés pour effectuer une conversion définie par l'utilisateur, les fonctions de conversion et les constructeurs sont tous deux pris en compte par la résolution de surcharge dans les contextes d' initialisation par copie et d' initialisation par référence , mais seuls les constructeurs sont pris en compte dans les contextes d' initialisation directe .
struct To { To() = default; To(const struct From&) {} // constructeur de conversion }; struct From { operator To() const {return To();} // fonction de conversion }; int main() { From f; To t1(f); // initialisation directe : appelle le constructeur // Note : si le constructeur de conversion n'est pas disponible, le constructeur de copie implicite // sera sélectionné, et la fonction de conversion sera appelée pour préparer son argument // To t2 = f; // initialisation par copie : ambiguë // Note : si la fonction de conversion est d'un type non-const, par exemple // From::operator To();, elle sera sélectionnée à la place du constructeur dans ce cas To t3 = static_cast<To>(f); // initialisation directe : appelle le constructeur const To& r = f; // initialisation de référence : ambiguë }
Fonction de conversion vers sa propre classe (éventuellement qualifiée cv) (ou vers une référence à celle-ci), vers la base de sa propre classe (ou vers une référence à celle-ci), et vers le type void peut être définie, mais ne peut pas être exécutée dans le cadre de la séquence de conversion, sauf, dans certains cas, via une résolution virtuelle :
struct D; struct B { virtual operator D() = 0; }; struct D : B { operator D() override { return D(); } }; int main() { D obj; D obj2 = obj; // n'appelle pas D::operator D() B& br = obj; D obj3 = br; // appelle D::operator D() via dispatch virtuel }
Il peut également être appelé en utilisant la syntaxe d'appel de fonction membre :
struct B {}; struct X : B { operator B&() { return *this; }; }; int main() { X x; B& b1 = x; // n'appelle pas X::operatorB&() B& b2 = static_cast<B&>(x); // n'appelle pas X::operatorB& B& b3 = x.operator B&(); // appelle X::operatorB& }
Lors d'un appel explicite à la fonction de conversion, conversion-type-id est gourmand : il s'agit de la plus longue séquence de tokens qui pourrait potentiellement former un conversion-type-id (incluant les attributs, le cas échéant) (depuis C++11) :
& x.operator int * a; // erreur : analysé comme & (x.operator int*) a, // et non comme & (x.operator int) * a operator int [[noreturn]] (); // erreur : attribut noreturn appliqué à un type
|
Le spécificateur de substitution auto peut être utilisé dans le conversion-type-id , indiquant un type de retour déduit : struct X { operator int(); // OK operator auto() -> short; // error: trailing return type not part of syntax operator auto() const { return 10; } // OK: deduced return type operator decltype(auto)() const { return 10l; } // OK: deduced return type }; Remarque : une fonction de conversion template ne peut pas avoir un type de retour déduit. |
(depuis C++14) |
Les fonctions de conversion peuvent être héritées et peuvent être virtual , mais ne peuvent pas être static . Une fonction de conversion dans la classe dérivée ne masque pas une fonction de conversion dans la classe de base, sauf si elles convertissent vers le même type.
La fonction de conversion peut être une fonction membre template, par exemple,
std::auto_ptr<T>::operator auto_ptr<Y>
. Voir
member template
et
template argument deduction
pour les règles spéciales applicables.
Mots-clés
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é |
|---|---|---|---|
| CWG 296 | C++98 | les fonctions de conversion pouvaient être statiques | elles ne peuvent pas être déclarées statiques |
| CWG 2016 | C++98 |
les fonctions de conversion ne pouvaient pas spécifier de types de retour,
mais les types sont présents dans conversion-type-id |
les types de retour ne peuvent pas être spécifiés dans les
spécificateurs de déclaration des fonctions de conversion |
| CWG 2175 | C++11 |
il n'était pas clair si le
[
[
noreturn
]
]
dans
operator int [ [ noreturn ] ] ( ) ; est analysé comme une partie du noptr-declarator (du déclarateur de fonction) ou du conversion-type-id |
il est analysé comme une partie du
conversion-type-id |