Namespaces
Variants

Overload resolution

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

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 :

  1. Construction de l'ensemble des fonctions candidates .
  2. Réduction de l'ensemble aux seules fonctions viables .
  3. 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).

Table des matières

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 de T . 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 cv T .
  • 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 vers T ou vers une classe dérivée de T , * this est utilisé comme argument objet implicite. Sinon (si this n'est pas dans la portée ou ne pointe pas vers T ), un objet fictif de type T est 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 T sont 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 explicit dans T ou dans une base de T (sauf si masquée), dont les qualificateurs cv sont identiques ou supérieurs à ceux de T , 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 :

1) candidats membres : si 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.
2) candidats non-membres : Pour les opérateurs où la surcharge d'opérateur autorise les formes non-membres, toutes les déclarations trouvées par la recherche de nom non qualifiée de 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)
3) candidats intégrés : Pour operator, , l'opérateur unaire operator & , et operator - > , l'ensemble des candidats intégrés est vide. Pour les autres opérateurs, les candidats intégrés sont ceux listés dans les pages des opérateurs intégrés tant que tous les opérandes peuvent être implicitement convertis vers leurs paramètres. Si un candidat intégré a la même liste de paramètres qu'un candidat non-membre ou un candidat non-membre réécrit (depuis C++20) qui n'est pas une spécialisation de modèle de fonction, il n'est pas ajouté à la liste des candidats intégrés. Lorsque les opérateurs d'affectation intégrés sont considérés, les conversions de leurs premiers paramètres sont restreintes : seules les séquences de conversion standard sont considérées.
4) candidats réécrits :
  • Pour les quatre expressions d'opérateurs relationnels x < y , x <= y , x > y , et x >= y , tous les operator <=> membres, non-membres et intégrés trouvés sont ajoutés à l'ensemble.
  • Pour les quatre expressions d'opérateurs relationnels x < y , x <= y , x > y , et x >= y ainsi que l'expression de comparaison à trois voies x <=> y , un candidat synthétisé avec l'ordre des deux paramètres inversé est ajouté pour chaque operator <=> membre, non-membre et intégré trouvé.
  • Pour x ! = y , tous les operator == membres, non-membres et intégrés trouvés sont ajoutés à l'ensemble, sauf s'il existe un operator ! = correspondant.
  • Pour les expressions d'opérateurs d'égalité x == y et x ! = y , un candidat synthétisé avec l'ordre des deux paramètres inversé est ajouté pour chaque operator == membre, non-membre et intégré trouvé, sauf s'il existe un operator ! = correspondant.
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 @ , x @ y est interprété comme l'expression réécrite : 0 @ ( y <=> x ) si le candidat sélectionné est un candidat synthétisé avec l'ordre inversé des paramètres, ou ( x <=> y ) @ 0 sinon, en utilisant le candidat réécrit operator <=> sélectionné.

Si un candidat réécrit operator == est sélectionné par la résolution de surcharge pour un opérateur @ (qui est soit == soit != ), son type de retour doit être (éventuellement qualifié cv) bool , et x @ y est interprété comme l'expression réécrite : y == x ou ! ( y == x ) si le candidat sélectionné est un candidat synthétisé avec l'ordre inversé des paramètres, ou ! ( x == y ) sinon, en utilisant le candidat réécrit operator == sélectionné.

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 explicit constructeur est choisi, l'initialisation est mal formée.

(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 explicit de S et de ses classes de base (sauf si masquées) vers T ou une classe dérivée de T ou une référence à celles-ci. Si cette initialisation par copie fait partie de la séquence d'initialisation directe de cv T (initialisant une référence à lier au premier paramètre d'un constructeur qui prend une référence à cv T ), 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 S et de ses classes de base (sauf si masquées) qui produisent le type T ou un type convertible en T par 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 S et de ses classes de base (sauf si masquées) qui produisent le type T ou un type convertible en T par 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 S et 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 T2 ou référence de rvalue vers cv2 T2
cv2 T2 est compatible avec les références avec cv1 T .
  • Pour l'initialisation directe, les fonctions de conversion définies par l'utilisateur explicites sont également considérées si T2 est du même type que T ou peut être converti en type T avec 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 T et 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 T et 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 C dont le premier paramètre est de type « référence vers P » (y compris un tel constructeur instancié à partir d'un modèle) est exclu de l'ensemble des fonctions candidates lors de la construction d'un objet de type D si toutes les conditions suivantes sont satisfaites :

  • La liste d'arguments contient exactement un argument.
  • C est référence-lié à P .
  • P est référence-lié à D .
(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 :

1) les conversions définies par l'utilisateur ne peuvent pas être appliquées au paramètre d'objet implicite
2) les rvalues peuvent être liées au paramètre objet implicite non-const (sauf s'il s'agit d'une fonction membre avec ref-qualification) (depuis C++11) et n'affectent pas le classement des conversions implicites.
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 :

1) S'il y a M arguments, la fonction candidate qui possède exactement M paramètres est viable
2) Si la fonction candidate a moins de M paramètres, mais possède un paramètre ellipsis , elle est viable.
3) Si la fonction candidate a plus de 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)
5) Pour chaque argument, il doit y avoir au moins une séquence de conversion implicite qui le convertit en le paramètre correspondant.
6) Si un paramètre a un type référence, la liaison de référence est prise en compte à cette étape : si un argument rvalue correspond à un paramètre de référence lvalue non const ou si un argument lvalue correspond à un paramètre de référence rvalue, la fonction n'est pas viable.

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

  • initialisation par list-initialization où la liste d'initialiseurs a exactement un élément qui est lui-même une liste d'initialiseurs, et la cible est le premier paramètre d'un constructeur de classe X , et la conversion est vers X ou référence vers (éventuellement cv-qualifié) X :
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

1) il y a au moins un argument de F1 dont la conversion implicite est meilleure que la conversion implicite correspondante pour cet argument de F2 , ou, sinon,
2) (uniquement dans le contexte de l'initialisation non-classe par conversion), la séquence de conversion standard du résultat de 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)
4) F1 est une fonction non-template tandis que F2 est une spécialisation de template, ou, si ce n'est pas le cas,
5) 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,
11) F1 est le candidat de déduction de copie 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 :

1) Correspondance exacte : aucune conversion requise, conversion lvalue-vers-rvalue, conversion de qualification, conversion de pointeur de fonction, (depuis C++17) conversion définie par l'utilisateur du type classe vers la même classe
2) Promotion : promotion entière, promotion en virgule flottante
3) Conversion : conversion intégrale, conversion en virgule flottante, conversion virgule flottante-intégrale, conversion de pointeur, conversion de pointeur vers membre, conversion booléenne, conversion définie par l'utilisateur d'une classe dérivée vers sa classe de base

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.

1) Une séquence de conversion standard est toujours meilleure qu'une séquence de conversion définie par l'utilisateur ou une séquence de conversion ellipsis.
2) Une séquence de conversion définie par l'utilisateur est toujours meilleure qu'une séquence de conversion ellipsis .
3) Une séquence de conversion standard S1 est meilleure qu'une séquence de conversion standard S2 si
a) 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,
b) le rang de S1 est meilleur que le rang de S2 , ou, si ce n'est pas le cas,
c) les deux 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&
ou, si ce n'est pas cela,
d) les deux 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
ou, si ce n'est pas cela,
e) S1 et S2 ne diffèrent que par la conversion de qualification, et

la qualification cv du résultat de S1 est un sous-ensemble propre de la qualification cv du résultat de S2 , et S1 n'est pas la conversion dépréciée tableau-vers-pointeur des littéraux de chaîne (jusqu'en C++11) .

(jusqu'en C++20)

le résultat de S1 peut être converti vers le résultat de S2 par une conversion de qualification.

(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*)
ou, si ce n'est pas cela,
f) les deux 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
ou, si ce n'est pas cela,
g) 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
4) Une séquence de conversion définie par l'utilisateur 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)
5) Une séquence d'initialisation de liste 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
  • le nombre d'éléments N1 initialisé par L1 est inférieur au nombre d'éléments N2 initialisé par L2, soit
  • N1 est égal à N2 et L2 se convertit en un tableau de taille inconnue et L1 ne le fait pas.
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 :

1) La conversion qui n'implique pas de pointeur vers bool ou de pointeur-sur-membre vers bool est meilleure que celle qui l'implique.
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)
4) La conversion qui transforme un pointeur-vers-dérivé en pointeur-vers-base est meilleure que la conversion d'un pointeur-vers-dérivé en pointeur-vers- void , et la conversion d'un pointeur-vers-base en void est meilleure que celle d'un pointeur-vers-dérivé en void .
5) Si Mid est dérivé (directement ou indirectement) de Base , et Derived est dérivé (directement ou indirectement) de Mid
a) Derived * vers Mid * est meilleur que Derived * vers Base *
b) Derived vers Mid & ou Mid && est meilleur que Derived vers Base & ou Base &&
c) Base :: * vers Mid :: * est meilleur que Base :: * vers Derived :: *
d) Derived vers Mid est meilleur que Derived vers Base
e) Mid * vers Base * est meilleur que Derived * vers Base *
f) Mid vers Base & ou Base && est meilleur que Derived vers Base & ou Base &&
g) Mid :: * vers Derived :: * est meilleur que Base :: * vers Derived :: *
h) 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 X et 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) en T est celle utilisée.
  • Sinon, si le type du paramètre est « tableau de limite inconnue de T » (cela se produit uniquement pour les références aux tableaux), la pire conversion implicite nécessaire pour convertir chaque élément de la liste en T est 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é
  1. 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]

Voir aussi