Overload resolution
Afin de compiler un appel de fonction, le compilateur doit d'abord effectuer la recherche de nom , qui, pour les fonctions, peut impliquer la recherche dépendante des arguments , et pour les templates de fonction peut être suivie par la déduction d'arguments de template .
Si le nom fait référence à plus d'une entité, on dit qu'il est surchargé , et le compilateur doit déterminer quelle surcharge appeler. En termes simples, la surcharge dont les paramètres correspondent le plus étroitement aux arguments est celle qui est appelée.
En détail, la résolution de surcharge procède par les étapes suivantes :
- Construction de l'ensemble des fonctions candidates .
- Réduction de l'ensemble aux seules fonctions viables .
- Analyse de l'ensemble pour déterminer la meilleure fonction viable unique (cela peut impliquer le classement des séquences de conversion implicite ).
void f(long); void f(float); f(0L); // appelle f(long) f(0); // erreur : surcharge ambiguë
En plus des appels de fonction, les noms de fonctions surchargées peuvent apparaître dans plusieurs autres contextes, où différentes règles s'appliquent : voir Adresse d'une fonction surchargée .
Si une fonction ne peut pas être sélectionnée par la résolution de surcharge, elle ne peut pas être utilisée (par exemple, il s'agit d'une entité templatée avec une contrainte échouée).
Fonctions candidates
Avant que la résolution de surcharge ne commence, les fonctions sélectionnées par la recherche de nom et la déduction d'argument de template sont combinées pour former l'ensemble des fonctions candidates . Les détails exacts dépendent du contexte dans lequel la résolution de surcharge aura lieu.
Appel à une fonction nommée
Si E dans une expression d'appel de fonction E ( args ) désigne un ensemble de fonctions surchargées et/ou de modèles de fonction (mais pas d'objets appelables), les règles suivantes sont appliquées :
-
Si l'expression
E
a la forme
PA
-
>
B
ou
A.
B
(où
A
a un type classe
cv
T), alors B est recherché en tant que fonction membre deT. Les déclarations de fonctions trouvées par cette recherche sont les fonctions candidates. La liste d'arguments pour la résolution de surcharge possède l'argument objet implicite de type cvT. - Si l'expression E est une expression primaire , le nom est recherché suivant les règles normales pour les appels de fonction (ce qui peut impliquer une ADL ). Les déclarations de fonctions trouvées par cette recherche sont (en raison du fonctionnement de la recherche) soit :
-
- toutes les fonctions non-membres (auquel cas la liste d'arguments pour la résolution de surcharge est exactement la liste d'arguments utilisée dans l'expression d'appel de fonction)
-
toutes les fonctions membres d'une classe
T, auquel cas, si this est dans la portée et est un pointeur versTou vers une classe dérivée deT, * this est utilisé comme argument objet implicite. Sinon (si this n'est pas dans la portée ou ne pointe pas versT), un objet fictif de typeTest utilisé comme argument objet implicite, et si la résolution de surcharge sélectionne ensuite une fonction membre non-statique, le programme est mal formé.
Appel à un objet de classe
Si
E
dans une
expression d'appel de fonction
E
(
args
)
a un type classe
cv
T
, alors
-
Les opérateurs d'appel de fonction de
Tsont obtenus par recherche ordinaire du nom operator ( ) dans le contexte de l'expression ( E ) . operator ( ) , et chaque déclaration trouvée est ajoutée à l'ensemble des fonctions candidates. -
Pour chaque
fonction de conversion définie par l'utilisateur
non
explicitdansTou dans une base deT(sauf si masquée), dont les qualificateurs cv sont identiques ou supérieurs à ceux deT, et où la fonction de conversion convertit vers :
-
- pointeur-vers-fonction
- référence-vers-pointeur-vers-fonction
- référence-vers-fonction
- puis une fonction d'appel substitut avec un nom unique dont le premier paramètre est le résultat de la conversion, les paramètres restants sont la liste des paramètres acceptés par le résultat de la conversion, et le type de retour est le type de retour du résultat de la conversion, est ajoutée à l'ensemble des fonctions candidates. Si cette fonction substitut est sélectionnée par la résolution de surcharge ultérieure, alors la fonction de conversion définie par l'utilisateur sera appelée et ensuite le résultat de la conversion sera appelé.
Dans tous les cas, la liste d'arguments à des fins de résolution de surcharge est la liste d'arguments de l'expression d'appel de fonction précédée de l'argument objet implicite E (lors de la correspondance avec la fonction substitut, la conversion définie par l'utilisateur convertira automatiquement l'argument objet implicite en premier argument de la fonction substitut).
int f1(int); int f2(float); struct A { using fp1 = int(*)(int); operator fp1() { return f1; } // fonction de conversion vers pointeur de fonction using fp2 = int(*)(float); operator fp2() { return f2; } // fonction de conversion vers pointeur de fonction } a; int i = a(1); // appelle f1 via le pointeur retourné par la fonction de conversion
Appel à un opérateur surchargé
Si au moins un des arguments d'un opérateur dans une expression a un type classe ou un type énumération, les opérateurs intégrés et les surcharges d'opérateurs définies par l'utilisateur participent à la résolution de surcharge, l'ensemble des fonctions candidates étant sélectionné comme suit :
Pour un opérateur unaire
@
dont l'argument est de type
T1
(après suppression des qualifications cv), ou un opérateur binaire
@
dont l'opérande gauche est de type
T1
et l'opérande droit de type
T2
(après suppression des qualifications cv), les ensembles suivants de fonctions candidates sont préparés :
T1
est une classe complète ou une classe en cours de définition, l'ensemble des candidats membres est le résultat de la
recherche de nom qualifiée
de
T1::operator@
. Dans tous les autres cas, l'ensemble des candidats membres est vide.
operator@
dans le contexte de l'expression (ce qui peut impliquer
ADL
), sauf que les déclarations de fonctions membres sont ignorées et n'empêchent pas la recherche de continuer dans la portée englobante suivante. Si les deux opérandes d'un opérateur binaire ou le seul opérande d'un opérateur unaire ont un type énumération, les seules fonctions de l'ensemble de recherche qui deviennent des candidats non-membres sont celles dont le paramètre a ce type énumération (ou une référence à ce type énumération)
|
4)
candidats réécrits
:
Dans tous les cas, les candidats réécrits ne sont pas considérés dans le contexte de l'expression réécrite. Pour tous les autres opérateurs, l'ensemble des candidats réécrits est vide.
|
(depuis C++20) |
L'ensemble des fonctions candidates à soumettre pour la résolution de surcharge est une union des ensembles ci-dessus. La liste d'arguments pour la résolution de surcharge comprend les opérandes de l'opérateur, à l'exception de
operator->
, où le second opérande n'est pas un argument pour l'appel de fonction (voir
opérateur d'accès membre
).
struct A { operator int(); // conversion définie par l'utilisateur }; A operator+(const A&, const A&); // opérateur non-membre défini par l'utilisateur void m() { A a, b; a + b; // candidats membres : aucun // candidats non-membres : operator+(a, b) // candidats intégrés : int(a) + int(b) // la résolution de surcharge choisit operator+(a, b) }
Si la résolution de surcharge sélectionne un candidat intégré, la séquence de conversion définie par l'utilisateur à partir d'un opérande de type classe n'est pas autorisée à avoir une seconde séquence de conversion standard : la fonction de conversion définie par l'utilisateur doit donner directement le type d'opérande attendu :
struct Y { operator int*(); }; // Y est convertible en int* int *a = Y() + 100.0; // erreur : aucun opérateur+ entre pointeur et double
Pour operator, , l'opérateur unaire operator & , et operator - > , s'il n'existe aucune fonction viable (voir ci-dessous) dans l'ensemble des fonctions candidates, alors l'opérateur est réinterprété comme un opérateur intégré.
|
Si un candidat réécrit
operator
<=>
est sélectionné par la résolution de surcharge pour un opérateur
Si un candidat réécrit
operator
==
est sélectionné par la résolution de surcharge pour un opérateur
La résolution de surcharge dans ce cas a un critère de départage final qui préfère les candidats non réécrits aux candidats réécrits, et préfère les candidats réécrits non synthétisés aux candidats réécrits synthétisés. Cette recherche avec l'ordre inversé des arguments permet d'écrire simplement operator <=> ( std:: string , const char * ) et operator == ( std:: string , const char * ) pour générer toutes les comparaisons entre std::string et const char * , dans les deux sens. Voir comparaisons par défaut pour plus de détails. |
(depuis C++20) |
Initialisation par constructeur
Lorsqu'un objet de type classe est direct-initialized ou default-initialized (y compris la default-initialization dans le contexte de copy-list-initialization ) (depuis C++11) , les fonctions candidates sont tous les constructeurs de la classe en cours d'initialisation. La liste d'arguments est la liste d'expressions de l'initialiseur.
Sinon, les fonctions candidates sont tous les constructeurs de conversion de la classe en cours d'initialisation. La liste d'arguments est l'expression de l'initialiseur.
|
Pour l'initialisation par défaut dans le contexte de l'initialisation par liste de copie, si un
|
(depuis C++11) |
Initialisation par copie via conversion
Si
l'initialisation par copie
d'un objet de type classe nécessite qu'une conversion définie par l'utilisateur soit appelée pour convertir l'expression d'initialisation de type
cv
S
vers le type
cv
T
de l'objet en cours d'initialisation, les fonctions suivantes sont des fonctions candidates :
-
tous les
constructeurs de conversion
de
T -
les fonctions de conversion non
explicitdeSet de ses classes de base (sauf si masquées) versTou une classe dérivée deTou une référence à celles-ci. Si cette initialisation par copie fait partie de la séquence d'initialisation directe de cvT(initialisant une référence à lier au premier paramètre d'un constructeur qui prend une référence à cvT), alors les fonctions de conversion explicites sont également considérées.
Dans tous les cas, la liste d'arguments à des fins de résolution de surcharge consiste en un seul argument qui est l'expression d'initialisation, qui sera comparée au premier argument du constructeur ou à l'argument objet implicite de la fonction de conversion.
Initialisation de non-classe par conversion
Lorsque l'initialisation d'un objet de type non-classe
cv1
T
nécessite une
fonction de conversion définie par l'utilisateur
pour convertir à partir d'une expression d'initialisation de type classe
cv
S
, les fonctions suivantes sont candidates :
-
les fonctions de conversion définies par l'utilisateur non-explicites de
Set de ses classes de base (sauf si masquées) qui produisent le typeTou un type convertible enTpar une séquence de conversion standard , ou une référence à un tel type. Les qualificateurs cv sur le type de retour sont ignorés pour la sélection des fonctions candidates. -
s'il s'agit d'une
initialisation directe
, les fonctions de conversion explicites définies par l'utilisateur de
Set de ses classes de base (sauf si masquées) qui produisent le typeTou un type convertible enTpar une conversion de qualification , ou une référence à un tel type, sont également considérées.
Dans tous les cas, la liste d'arguments à des fins de résolution de surcharge consiste en un seul argument qui est l'expression d'initialisation, qui sera comparée à l'argument objet implicite de la fonction de conversion.
Initialisation de référence par conversion
Lors de
l'initialisation de référence
, où la référence vers
cv1
T
est liée au résultat lvalue ou rvalue d'une conversion de l'expression d'initialisation du type classe
cv2
S
, les fonctions suivantes sont sélectionnées pour l'ensemble des candidats :
-
Les fonctions de conversion définies par l'utilisateur non-explicites de
Set de ses classes de base (sauf si masquées) vers le type
-
-
(lors de la conversion en une lvalue) référence de lvalue vers
cv2
T2 -
(lors de la conversion en une rvalue ou une lvalue de type fonction)
cv2
T2ou référence de rvalue vers cv2T2
-
(lors de la conversion en une lvalue) référence de lvalue vers
cv2
-
où
cv2
T2est compatible avec les références avec cv1T.
-
Pour l'initialisation directe, les fonctions de conversion définies par l'utilisateur explicites sont également considérées si
T2est du même type queTou peut être converti en typeTavec une conversion de qualification.
Dans tous les cas, la liste d'arguments à des fins de résolution de surcharge consiste en un seul argument qui est l'expression d'initialisation, qui sera comparée à l'argument objet implicite de la fonction de conversion.
Initialisation par liste
Lorsqu'un objet de type classe non-aggregat
T
est
initialisé par liste
, une résolution de surcharge en deux phases a lieu.
-
à la phase 1, les fonctions candidates sont tous les constructeurs de liste d'initialisation de
Tet la liste d'arguments pour la résolution de surcharge consiste en un seul argument de liste d'initialisation -
si la résolution de surcharge échoue à la phase 1, la phase 2 est entamée, où les fonctions candidates sont tous les constructeurs de
Tet la liste d'arguments pour la résolution de surcharge consiste en les éléments individuels de la liste d'initialisation.
Si la liste d'initialisation est vide et que
T
a un constructeur par défaut, la phase 1 est ignorée.
Dans l'initialisation de liste par copie, si la phase 2 sélectionne un constructeur explicite, l'initialisation est mal formée (contrairement à toutes les autres initialisations par copie où les constructeurs explicites ne sont même pas considérés).
Règles supplémentaires pour les candidats de modèles de fonction
Si la recherche de nom a trouvé un modèle de fonction, la déduction d'arguments de modèle et la vérification de tout argument de modèle explicite sont effectuées pour trouver les valeurs d'arguments de modèle (le cas échéant) qui peuvent être utilisées dans ce cas :
- Si les deux réussissent, les arguments du template sont utilisés pour synthétiser les déclarations des spécialisations de fonction template correspondantes, qui sont ajoutées à l'ensemble des candidats, et ces spécialisations sont traitées comme des fonctions non-template sauf indication contraire dans les règles de départage ci-dessous.
- Si la déduction d'arguments échoue ou si la spécialisation de fonction template synthétisée serait mal formée, aucune telle fonction n'est ajoutée à l'ensemble des candidats (voir SFINAE ).
Si un nom fait référence à une ou plusieurs fonctions modèles et également à un ensemble de fonctions non modèles surchargées, ces fonctions et les spécialisations générées à partir des modèles sont toutes des candidates.
Voir la surcharge de modèles de fonction pour plus de détails.
|
Si un constructeur template ou une fonction de conversion template possède un spécificateur explicite conditionnel qui s'avère être dépendant de la valeur , après déduction, si le contexte requiert un candidat qui n'est pas explicite et que la spécialisation générée est explicite, elle est retirée de l'ensemble des candidats. |
(depuis C++20) |
Règles supplémentaires pour les candidats constructeurs
|
Les constructeurs de déplacement et les opérateurs d'affectation par déplacement définis par défaut et déclarés supprimés sont exclus de l'ensemble des fonctions candidates.
Un constructeur
hérité
d'un type de classe
|
(depuis C++11) |
Règles supplémentaires pour les candidats de fonctions membres
Si une fonction candidate est une fonction membre (statique ou non statique) qui ne possède pas de paramètre objet explicite (depuis C++23) , mais n'est pas un constructeur, elle est traitée comme si elle avait un paramètre supplémentaire ( paramètre objet implicite ) qui représente l'objet pour lequel elle est appelée et apparaît avant le premier des paramètres réels.
De même, l'objet sur lequel une fonction membre est appelée est ajouté au début de la liste d'arguments en tant que implied object argument .
Pour les fonctions membres de la classe
X
, le type du paramètre objet implicite est affecté par les qualifications cv et les qualifications de référence de la fonction membre comme décrit dans
les fonctions membres
.
Les fonctions de conversion définies par l'utilisateur sont considérées comme des membres de l' argument objet implicite aux fins de détermination du type du paramètre objet implicite .
Les fonctions membres introduites par une déclaration using dans une classe dérivée sont considérées comme des membres de la classe dérivée pour définir le type du paramètre objet implicite .
|
Pour les fonctions membres statiques, le paramètre objet implicite est considéré comme correspondant à n'importe quel objet : son type n'est pas examiné et aucune séquence de conversion n'est tentée pour celui-ci. |
(jusqu'à C++23) |
Pour le reste de la résolution de surcharge, l' argument objet implicite est indiscernable des autres arguments, mais les règles spéciales suivantes s'appliquent au paramètre objet implicite :
struct B { void f(int); }; struct A { operator B&(); }; A a; a.B::f(1); // Error: user-defined conversions cannot be applied // to the implicit object parameter static_cast<B&>(a).f(1); // OK
Fonctions viables
Étant donné l'ensemble des fonctions candidates, construit comme décrit ci-dessus, l'étape suivante de la résolution de surcharge consiste à examiner les arguments et paramètres pour réduire l'ensemble à l'ensemble des fonctions viables
Pour être incluse dans l'ensemble des fonctions viables, la fonction candidate doit satisfaire aux conditions suivantes :
M
arguments, la fonction candidate qui possède exactement
M
paramètres est viable
M
paramètres, mais possède un
paramètre ellipsis
, elle est viable.
M
paramètres et que le
M+1
-ième paramètre et tous les paramètres suivants ont des arguments par défaut, elle est viable. Pour le reste de la résolution de surcharge, la liste des paramètres est tronquée à
M
.
|
4)
Si la fonction a une
contrainte
associée, elle doit être satisfaite
|
(since C++20) |
Les conversions définies par l'utilisateur (à la fois les constructeurs de conversion et les fonctions de conversion définies par l'utilisateur) sont interdites de participer à une séquence de conversion implicite où cela rendrait possible l'application de plus d'une conversion définie par l'utilisateur. Plus précisément, elles ne sont pas considérées si la cible de la conversion est le premier paramètre d'un constructeur ou le paramètre objet implicite d'une fonction de conversion définie par l'utilisateur, et que ce constructeur/fonction de conversion définie par l'utilisateur est un candidat pour
- copie-initialisation d'une classe par conversion définie par l'utilisateur ,
- initialisation d'un type non-classe par une fonction de conversion ,
- initialisation par fonction de conversion pour la liaison directe de référence ,
- initialisation par constructeur durant la seconde étape (initialisation directe) de la copie-initialisation de classe,
struct A { A(int); }; struct B { B(A); }; B b{{0}}; // list-initialization of B // candidates: B(const B&), B(B&&), B(A) // {0} -> B&& not viable: would have to call B(A) // {0} -> const B&: not viable: would have to bind to rvalue, would have to call B(A) // {0} -> A viable. Calls A(int): user-defined conversion to A is not banned |
(depuis C++11) |
Fonction viable optimale
Pour chaque paire de fonctions viables
F1
et
F2
, les séquences de conversion implicite du
i
ième
argument vers le
i
ième
paramètre sont classées pour déterminer laquelle est meilleure (sauf le premier argument, l'
argument objet implicite
pour les fonctions membres statiques n'a aucun effet sur le classement)
F1
est déterminée comme étant une meilleure fonction que
F2
si les conversions implicites pour tous les arguments de
F1
sont
pas pires
que les conversions implicites pour tous les arguments de F2, et
F1
dont la conversion implicite est
meilleure
que la conversion implicite correspondante pour cet argument de
F2
, ou, sinon,
F1
vers le type initialisé est
meilleure
que la séquence de conversion standard du résultat de
F2
, ou, si ce n'est pas le cas,
|
3)
(seulement dans le contexte de l'initialisation par fonction de conversion pour la liaison directe de référence à un type de fonction), le résultat de
F1
est le même type de référence (lvalue ou rvalue) que la référence en cours d'initialisation, et le résultat de
F2
ne l'est pas, ou, si ce n'est pas le cas,
|
(depuis C++11) |
F1
est une fonction non-template tandis que
F2
est une spécialisation de template, ou, si ce n'est pas le cas,
F1
et
F2
sont toutes deux des spécialisations de modèle et
F1
est plus spécialisée selon les
règles de classement partiel pour les spécialisations de modèles
, ou, si ce n'est pas le cas,
|
6)
F1
et
F2
sont des fonctions non-template et
F1
est
plus contrainte par ordre partiel
que
F2
:
template<typename T = int> struct S { constexpr void f(); // #1 constexpr void f(this S&) requires true; // #2 }; void test() { S<> s; s.f(); // calls #2 }
, ou, si ce n'est pas le cas,
|
(depuis C++20) |
|
7)
F1
est un constructeur pour une classe
D
,
F2
est un constructeur pour une classe de base
B
de
D
, et pour tous les arguments les paramètres correspondants de
F1
et
F2
ont le même type :
struct A { A(int = 0); }; struct B: A { using A::A; B(); }; B b; // OK, B::B()
, ou, si ce n'est pas le cas,
|
(depuis C++11) |
|
8)
F2
est un candidat réécrit et
F1
ne l'est pas, ou, sinon,
9)
F1
et
F2
sont tous deux des candidats réécrits, et
F2
est un candidat réécrit synthétisé avec l'ordre inversé des paramètres et
F1
ne l'est pas, ou, sinon,
|
(depuis C++20) |
|
10)
F1
est généré à partir d'un
guide de déduction défini par l'utilisateur
et
F2
ne l'est pas, ou, si ce n'est pas le cas,
12)
F1
est généré à partir d'un constructeur non template et
F2
est généré à partir d'un constructeur template :
template<class T> struct A { using value_type = T; A(value_type); // #1 A(const A&); // #2 A(T, T, int); // #3 template<class U> A(int, T, U); // #4 }; // #5 est A(A), le candidat de déduction de copie A x(1, 2, 3); // utilise #3, généré à partir d'un constructeur non template template<class T> A(T) -> A<T>; // #6, moins spécialisé que #5 A a (42); // utilise #6 pour déduire A<int> et #1 pour initialiser A b = a; // utilise #5 pour déduire A<int> et #2 pour initialiser template<class T> A(A<T>) -> A<A<T>>; // #7, aussi spécialisé que #5 A b2 = a; // utilise #7 pour déduire A<A<int>> et #1 pour initialiser |
(depuis C++17) |
Ces comparaisons par paires sont appliquées à toutes les fonctions viables. Si exactement une fonction viable est meilleure que toutes les autres, la résolution de surcharge réussit et cette fonction est appelée. Sinon, la compilation échoue.
void Fcn(const int*, short); // surcharge #1 void Fcn(int*, int); // surcharge #2 int i; short s = 0; void f() { Fcn(&i, 1L); // 1er argument : &i -> int* est meilleur que &i -> const int* // 2ème argument : 1L -> short et 1L -> int sont équivalents // appelle Fcn(int*, int) Fcn(&i, 'c'); // 1er argument : &i -> int* est meilleur que &i -> const int* // 2ème argument : 'c' -> int est meilleur que 'c' -> short // appelle Fcn(int*, int) Fcn(&i, s); // 1er argument : &i -> int* est meilleur que &i -> const int* // 2ème argument : s -> short est meilleur que s -> int // aucun gagnant, erreur de compilation }
Si la meilleure fonction viable résout à une fonction pour laquelle plusieurs déclarations ont été trouvées, et si deux quelconques de ces déclarations se trouvent dans des portées différentes et spécifient un argument par défaut qui a rendu la fonction viable, le programme est mal formé.
namespace A { extern "C" void f(int = 5); } namespace B { extern "C" void f(int = 5); } using A::f; using B::f; void use() { f(3); // OK, l'argument par défaut n'a pas été utilisé pour la viabilité f(); // erreur : argument par défaut trouvé deux fois }
Classement des séquences de conversion implicite
Les séquences de conversion implicite argument-paramètre considérées par la résolution de surcharge correspondent aux conversions implicites utilisées dans l' initialisation par copie (pour les paramètres non-référence), sauf que lors de la considération de la conversion vers le paramètre objet implicite ou vers le côté gauche de l'opérateur d'affectation, les conversions qui créent des objets temporaires ne sont pas considérées. Lorsque le paramètre est le paramètre objet implicite d'une fonction membre statique, la séquence de conversion implicite est une séquence de conversion standard qui n'est ni meilleure ni pire qu'aucune autre séquence de conversion standard. (depuis C++23)
Chaque type de séquence de conversion standard se voit attribuer l'un des trois rangs :
Le rang de la séquence de conversion standard est le pire des rangs des conversions standards qu'elle contient (il peut y avoir jusqu'à trois conversions )
La liaison d'un paramètre de référence directement à l'expression d'argument est soit une identité soit une conversion dérivée-vers-base :
struct Base {}; struct Derived : Base {} d; int f(Base&); // surcharge #1 int f(Derived&); // surcharge #2 int i = f(d); // d -> Derived& a le rang Correspondance Exacte // d -> Base& a le rang Conversion // appelle f(Derived&)
Puisque le classement des séquences de conversion opère uniquement avec des types et des catégories de valeur, un bit field peut se lier à un argument de référence pour les besoins du classement, mais si cette fonction est sélectionnée, elle sera mal formée.
S1
est
meilleure
qu'une séquence de conversion standard
S2
si
S1
est une sous-séquence propre de
S2
, excluant les transformations de lvalue ; la séquence de conversion d'identité est considérée comme une sous-séquence de toute conversion non-identique, ou, si ce n'est pas le cas,
S1
est meilleur que le rang de
S2
, ou, si ce n'est pas le cas,
S1
et
S2
se lient à un paramètre de référence autre que le paramètre objet implicite d'une fonction membre avec ref-qualifier, et
S1
lie une référence rvalue à une rvalue tandis que
S2
lie une référence lvalue à une rvalue :
int i; int f1(); int g(const int&); // surcharge #1 int g(const int&&); // surcharge #2 int j = g(i); // lvalue int -> const int& est la seule conversion valide int k = g(f1()); // rvalue int -> const int&& meilleure que rvalue int -> const int&
S1
et
S2
se lient à un paramètre de référence et
S1
lie une référence lvalue à une fonction tandis que
S2
lie une référence rvalue à une fonction :
int f(void(&)()); // surcharge #1 int f(void(&&)()); // surcharge #2 void g(); int i1 = f(g); // appelle #1
S1
et
S2
ne diffèrent que par la conversion de qualification, et
|
la qualification cv du résultat de
|
(jusqu'en C++20) |
|
le résultat de
|
(depuis C++20) |
int f(const int*); int f(int*); int i; int j = f(&i); // &i -> int* est meilleur que &i -> const int*, appelle f(int*)
S1
et
S2
se lient à des paramètres de référence uniquement différents par leur qualification cv de haut niveau, et le type de
S1
est
moins
qualifié cv que celui de
S2
:
int f(const int &); // surcharge #1 int f(int &); // surcharge #2 (les deux références) int g(const int &); // surcharge #1 int g(int); // surcharge #2 int i; int j = f(i); // lvalue i -> int& est meilleur que lvalue int -> const int& // appelle f(int&) int k = g(i); // lvalue i -> const int& classe Correspondance Exacte // lvalue i -> rvalue int classe Correspondance Exacte // surcharge ambiguë : erreur de compilation
S1
et
S2
lient le même type de référence « référence vers
T
» et ont respectivement les types sources
V1
et
V2
, où la séquence de conversion standard de
V1
*
vers
T
*
est meilleure que la séquence de conversion standard de
V2
*
vers
T
*
:
struct Z {}; struct A { operator Z&(); operator const Z&(); // surcharge #1 }; struct B { operator Z(); operator const Z&&(); // surcharge #2 }; const Z& r1 = A(); // OK, utilise #1 const Z&& r2 = B(); // OK, utilise #2
U1
est
meilleure
qu'une séquence de conversion définie par l'utilisateur
U2
si elles appellent le même constructeur/fonction de conversion définie par l'utilisateur ou initialisent la même classe avec une initialisation par agrégat, et dans les deux cas la deuxième séquence de conversion standard dans
U1
est meilleure que la deuxième séquence de conversion standard dans
U2
struct A { operator short(); // fonction de conversion définie par l'utilisateur } a; int f(int); // surcharge #1 int f(float); // surcharge #2 int i = f(a); // A -> short, suivi de short -> int (rang Promotion) // A -> short, suivi de short -> float (rang Conversion) // appelle f(int)
L1
est
meilleure
qu'une séquence d'initialisation de liste
L2
si
L1
initialise un paramètre
std::initializer_list
alors que
L2
ne le fait pas.
void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // choisit #2 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo", "bar"}); } // choisit #4
|
6)
Une séquence d'initialisation de liste
L1
est
meilleure
que la séquence d'initialisation de liste
L2
si les paramètres correspondants sont des références à des tableaux, et L1 se convertit en type « tableau de N1 T », L2 se convertit en « tableau de N2 T », et N1 est plus petit que N2.
|
(depuis C++11)
(jusqu'à C++20) |
|
6)
Une séquence d'initialisation de liste
L1
est
meilleure
que la séquence d'initialisation de liste
L2
si les paramètres correspondants sont des références à des tableaux, et L1 et L2 se convertissent en tableaux de même type d'élément, et soit
void f(int (&&)[] ); // overload #1 void f(double (&&)[] ); // overload #2 void f(int (&&)[2]); // overload #3 f({1}); // #1: Better than #2 due to conversion, better than #3 due to bounds f({1.0}); // #2: double -> double is better than double -> int f({1.0, 2.0}); // #2: double -> double is better than double -> int f({1, 2}); // #3: -> int[2] is better than -> int[], // and int -> int is better than int -> double |
(depuis C++20) |
Si deux séquences de conversion sont indiscernables parce qu'elles ont le même rang, les règles supplémentaires suivantes s'appliquent :
|
2)
La conversion qui promeut une
énumération
dont le type sous-jacent est fixe vers son type sous-jacent est meilleure que celle qui promeut vers le type promu du type sous-jacent, si les deux types sont différents.
enum num : char { one = '0' }; std::cout << num::one; // '0', not 48 |
(depuis C++11) |
|
3)
Une conversion dans l'une ou l'autre direction entre le type à virgule flottante
FP1
et le type à virgule flottante
FP2
est meilleure qu'une conversion dans la même direction entre
FP1
et le type arithmétique
T3
si
int f(std::float32_t); int f(std::float64_t); int f(long long); float x; std::float16_t y; int i = f(x); // appelle f(std::float32_t) sur les implémentations où // float et std::float32_t ont des rangs de conversion égaux int j = f(y); // erreur : ambigu, pas de rang de conversion égal |
(depuis C++23) |
Mid
est dérivé (directement ou indirectement) de
Base
, et
Derived
est dérivé (directement ou indirectement) de
Mid
Derived
vers
Mid
&
ou
Mid
&&
est meilleur que
Derived
vers
Base
&
ou
Base
&&
Derived
vers
Mid
est meilleur que
Derived
vers
Base
Mid
vers
Base
&
ou
Base
&&
est meilleur que
Derived
vers
Base
&
ou
Base
&&
Mid
vers
Base
est meilleur que
Derived
vers
Base
Les séquences de conversion ambiguës sont classées comme des séquences de conversion définies par l'utilisateur car plusieurs séquences de conversion pour un argument ne peuvent exister que si elles impliquent différentes conversions définies par l'utilisateur :
class B; class A { A (B&);}; // constructeur de conversion class B { operator A (); }; // fonction de conversion définie par l'utilisateur class C { C (B&); }; // constructeur de conversion void f(A) {} // surcharge #1 void f(C) {} // surcharge #2 B b; f(b); // B -> A via ctor ou B -> A via fonction (conversion ambiguë) // b -> C via ctor (conversion définie par l'utilisateur) // les conversions pour la surcharge #1 et pour la surcharge #2 // sont indiscernables ; la compilation échoue
Séquence de conversion implicite dans l'initialisation de liste
Dans l'initialisation par liste , l'argument est un braced-init-list , qui n'est pas une expression, donc la séquence de conversion implicite vers le type du paramètre pour la résolution de surcharge est déterminée par les règles spéciales suivantes :
-
Si le type du paramètre est un agrégat
Xet que la liste d'initialisation contient exactement un élément de même classe ou d'une classe dérivée (éventuellement qualifiée cv), la séquence de conversion implicite est celle requise pour convertir l'élément au type du paramètre. - Sinon, si le type du paramètre est une référence à un tableau de caractères et que la liste d'initialisation possède un seul élément qui est un littéral de chaîne de type approprié, la séquence de conversion implicite est la conversion identité.
-
Sinon, si le type du paramètre est
std::
initializer_list
<
X
>
, et qu'il existe une conversion implicite non rétrécissante de chaque élément de la liste d'initialisation vers
X, la séquence de conversion implicite pour la résolution de surcharge est la pire conversion nécessaire. Si la liste d'initialisation entre accolades est vide, la séquence de conversion est la conversion identité.
struct A { A(std::initializer_list<double>); // #1 A(std::initializer_list<complex<double>>); // #2 A(std::initializer_list<std::string>); // #3 }; A a{1.0, 2.0}; // sélectionne #1 (rvalue double -> double : conversion identité) void g(A); g({"foo", "bar"}); // sélectionne #3 (lvalue const char[4] -> std::string : conversion définie par l'utilisateur)
-
Sinon, si le type du paramètre est « tableau de N T » (cela ne se produit que pour les références à des tableaux), la liste d'initialisation doit avoir N éléments ou moins, et la pire conversion implicite nécessaire pour convertir chaque élément de la liste (ou la paire vide d'accolades
{}si la liste est plus courte que N) enTest celle utilisée.
|
(depuis C++20) |
typedef int IA[3]; void h(const IA&); void g(int (&&)[]); h({1, 2, 3}); // conversion identité int->int g({1, 2, 3}); // identique depuis C++20
-
Sinon, si le type du paramètre est un type de classe non-agrégé
X, la résolution de surcharge sélectionne le constructeur C de X pour l'initialiser à partir de la liste d'initialisation d'arguments
-
- Si C n'est pas un constructeur de liste d'initialisation et que la liste d'initialisation a un seul élément de type X éventuellement qualifié cv, la séquence de conversion implicite a le rang Exact Match. Si la liste d'initialisation a un seul élément de type dérivé de X éventuellement qualifié cv, la séquence de conversion implicite a le rang Conversion. (notez la différence avec les agrégats : les agrégats s'initialisent directement depuis les listes d'initialisation à un seul élément avant de considérer l'initialisation d'agrégat , les non-agrégats considèrent les constructeurs initializer_list avant tout autre constructeur)
- sinon, la séquence de conversion implicite est une séquence de conversion définie par l'utilisateur avec la deuxième séquence de conversion standard étant une conversion identité.
Si plusieurs constructeurs sont viables mais qu'aucun n'est meilleur que les autres, la séquence de conversion implicite est la séquence de conversion ambiguë.
struct A { A(std::initializer_list<int>); }; void f(A); struct B { B(int, double); }; void g(B); g({'a', 'b'}); // appelle g(B(int, double)), conversion définie par l'utilisateur // g({1.0, 1,0}); // erreur : double->int est un rétrécissement, non autorisé dans l'initialisation par liste void f(B); // f({'a', 'b'}); // f(A) et f(B) sont toutes deux des conversions définies par l'utilisateur
- Sinon, si le type du paramètre est un agrégat qui peut être initialisé à partir de la liste d'initialisation selon l'initialisation d'agrégat , la séquence de conversion implicite est une séquence de conversion définie par l'utilisateur avec la deuxième séquence de conversion standard étant une conversion identité.
struct A { int m1; double m2; }; void f(A); f({'a', 'b'}); // appelle f(A(int, double)), conversion définie par l'utilisateur
- Sinon, si le paramètre est une référence, les règles d'initialisation des références s'appliquent
struct A { int m1; double m2; }; void f(const A&); f({'a', 'b'}); // temporaire créé, f(A(int, double)) appelé. Conversion définie par l'utilisateur
- Sinon, si le type du paramètre n'est pas une classe et que la liste d'initialisation a un élément, la séquence de conversion implicite est celle requise pour convertir l'élément en type de paramètre
- Sinon, si le type du paramètre n'est pas un type classe et si la liste d'initialisation n'a aucun élément, la séquence de conversion implicite est la conversion identité.
|
Si l'argument est une liste d'initialisation désignée et que le paramètre n'est pas une référence, une conversion n'est possible que si le paramètre a un type agrégé qui peut être initialisé à partir de cette liste d'initialisation selon les règles de l'initialisation agrégée , auquel cas la séquence de conversion implicite est une séquence de conversion définie par l'utilisateur dont la deuxième séquence de conversion standard est une conversion identité. Si, après la résolution de surcharge, l'ordre de déclaration des membres de l'agrégat ne correspond pas pour la surcharge sélectionnée, l'initialisation du paramètre sera mal formée. struct A { int x, y; }; struct B { int y, x; }; void f(A a, int); // #1 void f(B b, ...); // #2 void g(A a); // #3 void g(B b); // #4 void h() { f({.x = 1, .y = 2}, 0); // OK; calls #1 f({.y = 2, .x = 1}, 0); // error: selects #1, initialization of a fails // due to non-matching member order g({.x = 1, .y = 2}); // error: ambiguous between #3 and #4 }
|
(depuis C++20) |
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 1 | C++98 |
le comportement n'était pas spécifié lorsque la même
fonction avec potentiellement différents arguments par défaut (provenant de portées différentes) est sélectionnée |
le programme est incorrect
dans ce cas |
| CWG 83 | C++98 |
la séquence de conversion d'un littéral de chaîne
vers char * était meilleure que celle vers const char * bien que la première soit obsolète |
le rang de la conversion obsolète
est abaissé (elle a été supprimée dans C++11) |
| CWG 162 | C++98 |
il était invalide si l'ensemble de surcharge désigné par
F
contient
une fonction membre non statique dans le cas de
&F(args)
|
uniquement invalide si la résolution
de surcharge sélectionne une fonction membre non statique dans ce cas |
| CWG 233 | C++98 |
les références et les pointeurs étaient traités de manière incohérente dans
la résolution de surcharge avec les conversions définies par l'utilisateur |
ils sont traités
de manière cohérente |
| CWG 280 | C++98 |
les fonctions d'appel de substitution n'étaient pas ajoutées à
l'ensemble des fonctions candidates pour les fonctions de conversion déclarées dans les classes de base inaccessibles |
a supprimé la contrainte d'accessibilité,
le programme est mal formé si une fonction d'appel de substitution est sélectionnée et que la fonction de conversion correspondante ne peut pas être appelée |
| CWG 415 | C++98 |
lorsqu'un modèle de fonction est sélectionné comme
candidat, ses spécialisations étaient instanciées en utilisant la déduction d'arguments de modèle |
aucune instanciation n'aura lieu
dans ce cas, leurs déclarations seront synthétisées |
| CWG 495 | C++98 |
lorsque les conversions implicites pour les arguments sont
également bonnes, une fonction de conversion non-template était toujours meilleure qu'une fonction de conversion template, même si cette dernière peut avoir une meilleure séquence de conversion standard |
les séquences de conversion
standard sont comparées avant les niveaux de spécialisation |
| CWG 1307 | C++11 | la résolution de surcharge basée sur la taille des tableaux n'était pas spécifiée |
un tableau plus court est
meilleur lorsque possible |
| CWG 1328 | C++11 |
la détermination des fonctions candidates lors de
la liaison d'une référence à un résultat de conversion n'était pas claire |
clarifiée |
| CWG 1374 | C++98 |
la conversion de qualification était vérifiée avant la liaison
de référence lors de la comparaison des séquences de conversion standard |
inversé |
| CWG 1385 | C++11 |
une fonction de conversion définie par l'utilisateur non explicite déclarée avec
un qualificateur de référence n'avait pas de fonction de substitution correspondante |
elle a une fonction de
substitution correspondante |
| CWG 1467 | C++11 |
l'initialisation de liste de même type pour
les agrégats et les tableaux a été omise |
initialisation définie |
| CWG 1601 | C++11 |
conversion d'une énumération vers son type sous-jacent
ne privilégiait pas le type sous-jacent fixe |
le type fixe est privilégié
par rapport à celui vers lequel il promeut |
| CWG 1608 | C++98 |
l'ensemble des membres candidats d'un opérateur unaire
@
dont l'argument a le type
T1
était vide si
T1
est une classe en cours de définition
|
l'ensemble est le résultat de
la recherche de nom qualifié de
T1::operator@
dans ce cas
|
| CWG 1687 | C++98 |
lorsqu'un candidat intégré est sélectionné par la résolution de surcharge,
les opérandes subiraient une conversion sans restriction |
convertir uniquement les opérandes de type classe,
et désactiver la seconde séquence de conversion standard |
| CWG 2052 | C++98 |
les spécialisations de fonctions templates synthétisées mal formées pouvaient
être ajoutées à l'ensemble des candidats, rendant le programme mal formé |
elles ne sont pas ajoutées
à l'ensemble des candidats |
| CWG 2076 | C++11 |
la conversion définie par l'utilisateur est appliquée à l'initialiseur unique
dans une liste d'initialisation imbriquée lors de l'initialisation de liste en raison de la résolution de CWG issue 1467 |
non appliqué |
| CWG 2137 | C++11 |
constructeurs de liste d'initialisation perdus face aux constructeurs
de copie lors de l'initialisation de liste de
X
à partir de
{
X
}
|
les non-agrégats considèrent
d'abord les listes d'initialisation |
| CWG 2273 | C++11 |
il n'y avait pas de critère de départage entre
les constructeurs hérités et non hérités |
le constructeur non hérité l'emporte |
| CWG 2673 | C++20 |
les candidats intégrés avec la même liste de paramètres
qu'un candidat non-membre réécrit ont été ajoutés à la liste des candidats intégrés |
non ajouté |
| CWG 2712 | C++98 |
lorsqu'un opérateur d'affectation intégré est considéré,
le premier paramètre ne pouvait pas être lié à un temporaire, ce qui est déjà impossible [1] |
a supprimé l'exigence
redondante |
| CWG 2713 | C++20 |
la restriction de conversion concernant les listes d'initialisation désignées
était appliquée même si le paramètre est une référence |
non restreint dans ce cas |
| CWG 2789 | C++23 |
le paramètre objet explicite a été inclus
lors de la comparaison des listes de types de paramètres |
exclu |
| CWG 2856 | C++11 |
la résolution de surcharge pour l'initialisation par défaut dans le contexte
de l'initialisation de liste de copie ne considérait que les constructeurs de conversion |
considère tous les constructeurs |
| CWG 2919 | C++98 |
l'ensemble des candidats pour l'initialisation par référence par conversion
dépendait du type cible de l'initialisation |
dépend du type cible
de la conversion |
| P2468R2 | C++20 |
les candidats réécrits basés sur
operator
==
sont ajoutés
pour a ! = b même si un operator ! = correspondant existe |
non ajouté |
-
↑
Le type du premier paramètre d'un opérateur d'affectation intégré est « référence à lvalue vers
Téventuellement qualifié volatile ». Les références de ce type ne peuvent pas être liées à un temporaire.
Références
- Norme C++23 (ISO/CEI 14882:2024) :
-
- 12.2 Résolution de surcharge [over.match]
- Norme C++20 (ISO/IEC 14882:2020) :
-
- 12.4 Résolution de surcharge [over.match]
- Norme C++17 (ISO/CEI 14882:2017) :
-
- 16.3 Résolution de surcharge [over.match]
- Norme C++14 (ISO/CEI 14882:2014) :
-
- 13.3 Résolution de surcharge [over.match]
- Norme C++11 (ISO/IEC 14882:2011) :
-
- 13.3 Résolution de surcharge [over.match]
- Norme C++03 (ISO/IEC 14882:2003) :
-
- 13.3 Résolution de surcharge [over.match]