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

Para compilar una llamada a función, el compilador debe primero realizar name lookup , que, para funciones, puede involucrar argument-dependent lookup , y para plantillas de función puede ser seguido por template argument deduction .

Si el nombre se refiere a más de una entidad, se dice que está sobrecargado , y el compilador debe determinar qué sobrecarga llamar. En términos simples, la sobrecarga cuyos parámetros coinciden más estrechamente con los argumentos es la que se llama.

En detalle, la resolución de sobrecarga procede a través de los siguientes pasos:

  1. Construcción del conjunto de candidate functions .
  2. Reducción del conjunto a solo viable functions .
  3. Análisis del conjunto para determinar la única best viable function (esto puede involucrar ranking of implicit conversion sequences ).
void f(long);
void f(float);
f(0L); // llama a f(long)
f(0);  // error: sobrecarga ambigua

Además de las llamadas a funciones, los nombres de funciones sobrecargadas pueden aparecer en varios contextos adicionales, donde se aplican reglas diferentes: consulte Dirección de una función sobrecargada .

Si una función no puede ser seleccionada por la resolución de sobrecarga, no puede ser utilizada (por ejemplo, es una entidad con plantilla con una restricción fallida).

Contenidos

Funciones candidatas

Antes de que comience la resolución de sobrecarga, las funciones seleccionadas por la búsqueda de nombre y la deducción de argumentos de plantilla se combinan para formar el conjunto de funciones candidatas . Los detalles exactos dependen del contexto en el que tendrá lugar la resolución de sobrecarga.

Llamada a una función nombrada

Si E en una expresión de llamada a función E ( args ) nombra un conjunto de funciones sobrecargadas y/o plantillas de función (pero no objetos invocables), se siguen las siguientes reglas:

  • Si la expresión E tiene la forma PA - > B o A. B (donde A tiene tipo clase cv T ), entonces B se busca como una función miembro de T . Las declaraciones de función encontradas por esa búsqueda son las funciones candidatas. La lista de argumentos para el propósito de resolución de sobrecarga tiene el argumento objeto implícito de tipo cv T .
  • Si la expresión E es una expresión primaria , el nombre se busca siguiendo las reglas normales para llamadas a función (lo que puede involucrar ADL ). Las declaraciones de función encontradas por esta búsqueda son (debido a la forma en que funciona la búsqueda) ya sea:
  • todas las funciones no miembro (en cuyo caso la lista de argumentos para el propósito de resolución de sobrecarga es exactamente la lista de argumentos utilizada en la expresión de llamada a función)
  • todas las funciones miembro de alguna clase T , en cuyo caso, si this está en el ámbito y es un puntero a T o a una clase derivada de T , * this se utiliza como el argumento objeto implícito. En caso contrario (si this no está en el ámbito o no apunta a T ), se utiliza un objeto ficticio de tipo T como argumento objeto implícito, y si la resolución de sobrecarga selecciona posteriormente una función miembro no estática, el programa está mal formado.

Llamada a un objeto de clase

Si E en una expresión de llamada a función E ( args ) tiene tipo clase cv T , entonces

  • Los operadores de llamada a función de T se obtienen mediante la búsqueda ordinaria lookup del nombre operator ( ) en el contexto de la expresión ( E ) . operator ( ) , y cada declaración encontrada se añade al conjunto de funciones candidatas.
  • Para cada función de conversión user-defined conversion function no explicit en T o en una base de T (a menos que esté oculta), cuyos calificadores cv sean iguales o mayores que los calificadores cv de T , y donde la función de conversión convierte a:
  • pointer-to-function
  • reference-to-pointer-to-function
  • reference-to-function
entonces se añade al conjunto de funciones candidatas una función de llamada sustituta con un nombre único cuyo primer parámetro es el resultado de la conversión, los parámetros restantes son la lista de parámetros aceptada por el resultado de la conversión, y el tipo de retorno es el tipo de retorno del resultado de la conversión. Si esta función sustituta es seleccionada por la resolución de sobrecarga posterior, entonces se llamará a la función de conversión definida por el usuario y luego se llamará al resultado de la conversión.

En cualquier caso, la lista de argumentos para el propósito de resolución de sobrecarga es la lista de argumentos de la expresión de llamada a función precedida por el argumento de objeto implícito E (al coincidir con la función sustituta, la conversión definida por el usuario convertirá automáticamente el argumento de objeto implícito al primer argumento de la función sustituta).

int f1(int);
int f2(float);
struct A
{
    using fp1 = int(*)(int);
    operator fp1() { return f1; } // función de conversión a puntero a función
    using fp2 = int(*)(float);
    operator fp2() { return f2; } // función de conversión a puntero a función
} a;
int i = a(1); // llama a f1 mediante el puntero devuelto por la función de conversión

Llamada a un operador sobrecargado

Si al menos uno de los argumentos de un operador en una expresión tiene tipo clase o tipo enumeración, tanto los operadores incorporados como las sobrecargas de operadores definidas por el usuario participan en la resolución de sobrecarga, seleccionándose el conjunto de funciones candidatas de la siguiente manera:

Para un operador unario @ cuyo argumento tiene tipo T1 (después de eliminar calificadores cv), o un operador binario @ cuyo operando izquierdo tiene tipo T1 y operando derecho de tipo T2 (después de eliminar calificadores cv), se preparan los siguientes conjuntos de funciones candidatas:

1) candidatos de miembro : si T1 es una clase completa o una clase que se está definiendo actualmente, el conjunto de candidatos de miembro es el resultado de la búsqueda de nombre calificada de T1::operator@ . En todos los demás casos, el conjunto de candidatos de miembro está vacío.
2) candidatos no miembros : Para los operadores donde la sobrecarga de operadores permite formas no miembro, todas las declaraciones encontradas mediante la búsqueda de nombre no calificada de operator@ en el contexto de la expresión (que puede involucrar ADL ), excepto que las declaraciones de funciones miembro se ignoran y no evitan que la búsqueda continúe en el siguiente ámbito envolvente. Si ambos operandos de un operador binario o el único operando de un operador unario tienen tipo enumeración, las únicas funciones del conjunto de búsqueda que se convierten en candidatos no miembros son aquellas cuyo parámetro tiene ese tipo enumeración (o referencia a ese tipo enumeración)
3) candidatos incorporados : Para operator, , el operador unario operator & , y operator - > , el conjunto de candidatos incorporados está vacío. Para otros operadores, los candidatos incorporados son los listados en las páginas de operadores incorporados siempre que todos los operandos puedan convertirse implícitamente a sus parámetros. Si algún candidato incorporado tiene la misma lista de parámetros que un candidato no miembro o candidato no miembro reescrito (desde C++20) que no sea una especialización de plantilla de función, no se añade a la lista de candidatos incorporados. Cuando se consideran los operadores de asignación incorporados, las conversiones desde sus primeros parámetros están restringidas: solo se consideran las secuencias de conversión estándar .
4) candidatos reescritos :
  • Para las cuatro expresiones de operadores relacionales x < y , x <= y , x > y , y x >= y , todos los operator <=> miembros, no-miembros y built-in encontrados se añaden al conjunto.
  • Para las cuatro expresiones de operadores relacionales x < y , x <= y , x > y , y x >= y así como la expresión de comparación de tres vías x <=> y , se añade un candidato sintetizado con el orden de los dos parámetros invertido por cada operator <=> miembro, no-miembro y built-in encontrado.
  • Para x ! = y , todos los operator == miembros, no-miembros y built-in encontrados se añaden al conjunto, a menos que exista un operator ! = coincidente.
  • Para las expresiones de operadores de igualdad x == y y x ! = y , se añade un candidato sintetizado con el orden de los dos parámetros invertido por cada operator == miembro, no-miembro y built-in encontrado, a menos que exista un operator ! = coincidente.
En todos los casos, los candidatos reescritos no se consideran en el contexto de la expresión reescrita. Para todos los demás operadores, el conjunto de candidatos reescritos está vacío.
(desde C++20)

El conjunto de funciones candidatas a ser sometidas a resolución de sobrecarga es una unión de los conjuntos anteriores. La lista de argumentos para el propósito de resolución de sobrecarga consiste en los operandos del operador excepto para operator-> , donde el segundo operando no es un argumento para la llamada de función (ver operador de acceso a miembro ).

struct A
{
    operator int();              // conversión definida por el usuario
};
A operator+(const A&, const A&); // operador no miembro definido por el usuario
void m()
{
    A a, b;
    a + b; // candidatos miembro: ninguno
           // candidatos no miembro: operator+(a, b)
           // candidatos incorporados: int(a) + int(b)
           // la resolución de sobrecarga elige operator+(a, b)
}

Si la resolución de sobrecarga selecciona un candidato incorporado, la secuencia de conversión definida por el usuario desde un operando de tipo clase no puede tener una segunda secuencia de conversión estándar: la función de conversión definida por el usuario debe dar el tipo de operando esperado directamente:

struct Y { operator int*(); }; // Y es convertible a int*
int *a = Y() + 100.0;          // error: no existe operator+ entre puntero y double

Para el operator, , el operador unario operator & , y operator - > , si no hay funciones viables (ver más abajo) en el conjunto de funciones candidatas, entonces el operador se reinterpreta como incorporado.

Si un candidato reescrito de operator <=> es seleccionado por la resolución de sobrecarga para un operador @ , x @ y se interpreta como la expresión reescrita: 0 @ ( y <=> x ) si el candidato seleccionado es un candidato sintetizado con orden inverso de parámetros, o ( x <=> y ) @ 0 en caso contrario, utilizando el candidato reescrito seleccionado de operator <=> .

Si un candidato reescrito de operator == es seleccionado por la resolución de sobrecarga para un operador @ (que es == o != ), su tipo de retorno debe ser (posiblemente calificado cv) bool , y x @ y se interpreta como la expresión reescrita: y == x o ! ( y == x ) si el candidato seleccionado es un candidato sintetizado con orden inverso de parámetros, o ! ( x == y ) en caso contrario, utilizando el candidato reescrito seleccionado de operator == .

La resolución de sobrecarga en este caso tiene un desempate final que prefiere candidatos no reescritos sobre candidatos reescritos, y prefiere candidatos reescritos no sintetizados sobre candidatos reescritos sintetizados.

Esta búsqueda con orden de argumentos invertido hace posible escribir solo operator <=> ( std:: string , const char * ) y operator == ( std:: string , const char * ) para generar todas las comparaciones entre std::string y const char * , en ambas direcciones. Consulte comparaciones por defecto para más detalles.

(desde C++20)

Inicialización por constructor

Cuando un objeto de tipo clase es direct-initialized o default-initialized (incluyendo default-initialization en el contexto de copy-list-initialization ) (since C++11) , las funciones candidatas son todos los constructores de la clase que se está inicializando. La lista de argumentos es la lista de expresiones del inicializador.

De lo contrario, las funciones candidatas son todos los converting constructors de la clase que se está inicializando. La lista de argumentos es la expresión del inicializador.

Para la inicialización por defecto en el contexto de la inicialización de lista de copia, si se elige un explicit constructor, la inicialización es incorrecta.

(since C++11)

Inicialización por copia mediante conversión

Si la inicialización por copia de un objeto de tipo clase requiere que se llame a una conversión definida por el usuario para convertir la expresión inicializadora de tipo cv S al tipo cv T del objeto que se está inicializando, las siguientes funciones son funciones candidatas:

  • todos los constructores de conversión de T
  • las funciones de conversión no explicit de S y sus clases base (a menos que estén ocultas) a T o clase derivada de T o una referencia a tales. Si esta inicialización por copia es parte de la secuencia de inicialización directa de cv T (inicializando una referencia para vincularse al primer parámetro de un constructor que toma una referencia a cv T ), entonces también se consideran las funciones de conversión explícitas.

En cualquier caso, la lista de argumentos para el propósito de resolución de sobrecarga consiste en un único argumento que es la expresión de inicialización, que será comparada contra el primer argumento del constructor o contra el argumento de objeto implícito de la función de conversión.

Inicialización de no-clase por conversión

Cuando la inicialización de un objeto de tipo no-clase cv1 T requiere una función de conversión definida por el usuario para convertir desde una expresión inicializadora de tipo clase cv S , las siguientes funciones son candidatas:

  • las funciones de conversión definidas por el usuario no explícitas de S y sus clases base (a menos que estén ocultas) que producen el tipo T o un tipo convertible a T mediante una secuencia de conversión estándar , o una referencia a dicho tipo. Los calificadores cv en el tipo devuelto se ignoran para la selección de funciones candidatas.
  • si se trata de inicialización directa , también se consideran las funciones de conversión explícitas definidas por el usuario de S y sus clases base (a menos que estén ocultas) que producen el tipo T o un tipo convertible a T mediante una conversión de calificación , o una referencia a dicho tipo.

En cualquier caso, la lista de argumentos para el propósito de resolución de sobrecarga consiste en un único argumento que es la expresión de inicialización, que será comparada contra el argumento de objeto implícito de la función de conversión.

Inicialización de referencia por conversión

Durante la inicialización de referencia , donde la referencia a cv1 T se vincula al resultado lvalue o rvalue de una conversión desde la expresión inicializadora del tipo clase cv2 S , las siguientes funciones son seleccionadas para el conjunto de candidatos:

  • Las funciones de conversión definidas por el usuario no explícitas de S y sus clases base (a menos que estén ocultas) al tipo
  • (al convertir a un lvalue) referencia a lvalue a cv2 T2
  • (al convertir a un rvalue o un lvalue de tipo función) cv2 T2 o referencia a rvalue a cv2 T2
donde cv2 T2 es compatible con referencia con cv1 T .
  • Para la inicialización directa, las funciones de conversión definidas por el usuario explícitas también se consideran si T2 es del mismo tipo que T o puede convertirse al tipo T con una conversión de calificación.

En cualquier caso, la lista de argumentos para el propósito de resolución de sobrecarga consiste en un único argumento que es la expresión de inicialización, que será comparada contra el argumento de objeto implícito de la función de conversión.

Inicialización de lista

Cuando un objeto de tipo de clase no agregado T es inicializado por lista , tiene lugar una resolución de sobrecarga en dos fases.

  • en la fase 1, las funciones candidatas son todos los constructores de lista de inicialización de T y la lista de argumentos para el propósito de resolución de sobrecarga consiste en un único argumento de lista de inicialización
  • si la resolución de sobrecarga falla en la fase 1, se ingresa a la fase 2, donde las funciones candidatas son todos los constructores de T y la lista de argumentos para el propósito de resolución de sobrecarga consiste en los elementos individuales de la lista de inicialización.

Si la lista de inicializadores está vacía y T tiene un constructor predeterminado, se omite la fase 1.

En la inicialización de lista de copia, si la fase 2 selecciona un constructor explícito, la inicialización está mal formada (a diferencia de todas las demás inicializaciones de copia donde los constructores explícitos ni siquiera se consideran).

Reglas adicionales para candidatos de plantillas de función

Si la búsqueda de nombre encontró una plantilla de función, la deducción de argumentos de plantilla y la verificación de cualquier argumento de plantilla explícito se realizan para encontrar los valores de argumento de plantilla (si los hay) que pueden usarse en este caso:

  • Si ambos tienen éxito, los argumentos de plantilla se utilizan para sintetizar declaraciones de las especializaciones de plantilla de función correspondientes, que se agregan al conjunto de candidatos, y dichas especializaciones se tratan igual que funciones no plantilla excepto donde se especifique lo contrario en las reglas de desempate siguientes.
  • Si la deducción de argumentos falla o la especialización de plantilla de función sintetizada sería incorrecta, ninguna función de este tipo se agrega al conjunto de candidatos (ver SFINAE ).

Si un nombre se refiere a una o más plantillas de función y también a un conjunto de funciones no plantilla sobrecargadas, esas funciones y las especializaciones generadas a partir de las plantillas son todas candidatas.

Consulte function template overloading para más detalles.

Si una plantilla de constructor o plantilla de función de conversión tiene un especificador explícito condicional que resulta ser dependiente de valor , después de la deducción, si el contexto requiere un candidato que no sea explícito y la especialización generada es explícita, se elimina del conjunto de candidatos.

(since C++20)

Reglas adicionales para candidatos de constructor

Los constructores de movimiento y operadores de asignación de movimiento predeterminados que están definidos como eliminados se excluyen del conjunto de funciones candidatas.

Un constructor heredado del tipo de clase C que tiene un primer parámetro de tipo "referencia a P " (incluyendo dicho constructor instanciado desde una plantilla) se excluye del conjunto de funciones candidatas al construir un objeto de tipo D si se cumplen todas las siguientes condiciones:

  • La lista de argumentos tiene exactamente un argumento.
  • C está relacionado por referencia con P .
  • P está relacionado por referencia con D .
(desde C++11)

Reglas adicionales para candidatos a funciones miembro

Si cualquier función candidata es una función miembro (estática o no estática) que no tiene un parámetro de objeto explícito (desde C++23) , pero no es un constructor, se trata como si tuviera un parámetro adicional ( parámetro de objeto implícito ) que representa el objeto para el cual se llama y aparece antes del primero de los parámetros reales.

De manera similar, el objeto sobre el cual se está llamando a una función miembro se antepone a la lista de argumentos como el implied object argument .

Para las funciones miembro de la clase X , el tipo del parámetro de objeto implícito se ve afectado por las calificaciones cv y las calificaciones de referencia de la función miembro como se describe en member functions .

Las funciones de conversión definidas por el usuario se consideran miembros del implied object argument para determinar el tipo del implicit object parameter .

Las funciones miembro introducidas por una declaración using en una clase derivada se consideran miembros de la clase derivada para el propósito de definir el tipo del objeto implícito parámetro .

Para las funciones miembro estáticas, el parámetro de objeto implícito se considera que coincide con cualquier objeto: su tipo no se examina y no se intenta ninguna secuencia de conversión para él.

(until C++23)

Para el resto de la resolución de sobrecarga, el implied object argument es indistinguible de otros argumentos, pero las siguientes reglas especiales se aplican al implicit object parameter :

1) las conversiones definidas por el usuario no pueden aplicarse al parámetro de objeto implícito
2) los rvalues pueden vincularse al parámetro de objeto implícito no constante (a menos que sea para una función miembro calificada con ref) (desde C++11) y no afectan la clasificación de las conversiones implícitas.
struct B { void f(int); };
struct A { operator B&(); };
A a;
a.B::f(1); // Error: las conversiones definidas por el usuario no pueden aplicarse
           // al parámetro de objeto implícito
static_cast<B&>(a).f(1); // OK

Funciones viables

Dado el conjunto de funciones candidatas, construido como se describió anteriormente, el siguiente paso de la resolución de sobrecarga es examinar argumentos y parámetros para reducir el conjunto al conjunto de funciones viables

Para ser incluida en el conjunto de funciones viables, la función candidata debe satisfacer lo siguiente:

1) Si hay M argumentos, la función candidata que tiene exactamente M parámetros es viable
2) Si la función candidata tiene menos de M parámetros, pero tiene un parámetro de elipsis , es viable.
3) Si la función candidata tiene más de M parámetros y el parámetro M+1 y todos los parámetros siguientes tienen argumentos predeterminados, es viable. Para el resto de la resolución de sobrecarga, la lista de parámetros se trunca en M .
4) Si la función tiene una constraint asociada, debe ser satisfecha
(since C++20)
5) Para cada argumento debe existir al menos una secuencia de conversión implícita que lo convierta al parámetro correspondiente.
6) Si algún parámetro tiene tipo de referencia, el enlace de referencia se considera en este paso: si un argumento rvalue corresponde a un parámetro de referencia lvalue no constante o un argumento lvalue corresponde a un parámetro de referencia rvalue, la función no es viable.

Las conversiones definidas por el usuario (tanto constructores de conversión como funciones de conversión definidas por el usuario) están prohibidas de participar en secuencias de conversión implícita donde sería posible aplicar más de una conversión definida por el usuario. Específicamente, no se consideran si el destino de la conversión es el primer parámetro de un constructor o el parámetro de objeto implícito de una función de conversión definida por el usuario, y ese constructor/función de conversión definida por el usuario es candidato para

  • inicialización mediante lista-inicialización donde la lista de inicializadores tiene exactamente un elemento que es en sí mismo una lista de inicializadores, y el objetivo es el primer parámetro de un constructor de la clase X , y la conversión es a X o referencia a (posiblemente calificada cv) 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
(desde C++11)

Función viable óptima

Para cada par de funciones viables F1 y F2 , las secuencias de conversión implícita desde el i ésimo argumento al i ésimo parámetro se clasifican para determinar cuál es mejor (excepto el primer argumento, el argumento de objeto implícito para funciones miembro estáticas no tiene efecto en la clasificación)

F1 se determina que es una mejor función que F2 si las conversiones implícitas para todos los argumentos de F1 son no peores que las conversiones implícitas para todos los argumentos de F2, y

1) hay al menos un argumento de F1 cuya conversión implícita es mejor que la conversión implícita correspondiente para ese argumento de F2 , o, si no es así,
2) (solo en el contexto de inicialización no de clase por conversión), la secuencia de conversión estándar desde el resultado de F1 al tipo que se está inicializando es mejor que la secuencia de conversión estándar desde el resultado de F2 , o, si no es eso,
3) (solo en el contexto de inicialización mediante función de conversión para el enlace directo de referencia a un tipo de función), el resultado de F1 es el mismo tipo de referencia (lvalue o rvalue) que la referencia que se está inicializando, y el resultado de F2 no lo es, o, si no es eso,
(since C++11)
4) F1 es una función no plantilla mientras que F2 es una especialización de plantilla, o, si no es eso,
5) F1 y F2 son ambas especializaciones de plantilla y F1 está más especializada de acuerdo con las reglas de ordenamiento parcial para especializaciones de plantilla , o, si no es así,
6) F1 y F2 son funciones no plantilla y F1 está más parcialmente ordenada por restricciones 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(); // llama a #2
}
, o, si no es así,
(desde C++20)


7) F1 es un constructor para una clase D , F2 es un constructor para una clase base B de D , y para todos los argumentos los parámetros correspondientes de F1 y F2 tienen el mismo tipo:
struct A
{
    A(int = 0);
};
struct B: A
{
    using A::A;
    B();
};
B b; // OK, B::B()
, o, si no es así,
(desde C++11)


8) F2 es un candidato reescrito y F1 no lo es, o, si no es así,
9) F1 y F2 son ambos candidatos reescritos, y F2 es un candidato reescrito sintetizado con orden invertido de parámetros y F1 no lo es, o, si no es así,
(desde C++20)


10) F1 se genera a partir de una guía de deducción definida por el usuario y F2 no lo es, o, si no es así,
11) F1 es el candidato de deducción por copia y F2 no lo es, o, si no es así,
12) F1 se genera a partir de un constructor no plantilla y F2 se genera a partir de una plantilla de constructor:
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 es A(A), el candidato de deducción por copia
A x(1, 2, 3); // usa #3, generado a partir de un constructor no plantilla
template<class T>
A(T) -> A<T>;       // #6, menos especializada que #5
A a (42); // usa #6 para deducir A<int> y #1 para inicializar
A b = a;  // usa #5 para deducir A<int> y #2 para inicializar
template<class T>
A(A<T>) -> A<A<T>>; // #7, tan especializada como #5
A b2 = a; // usa #7 para deducir A<A<int>> y #1 para inicializar
(desde C++17)

Estas comparaciones por pares se aplican a todas las funciones viables. Si exactamente una función viable es mejor que todas las demás, la resolución de sobrecarga tiene éxito y esta función es llamada. De lo contrario, la compilación falla.

void Fcn(const int*, short); // sobrecarga #1
void Fcn(int*, int);         // sobrecarga #2
int i;
short s = 0;
void f() 
{
    Fcn(&i, 1L);  // 1er argumento: &i -> int* es mejor que &i -> const int*
                  // 2do argumento: 1L -> short y 1L -> int son equivalentes
                  // llama a Fcn(int*, int)
    Fcn(&i, 'c'); // 1er argumento: &i -> int* es mejor que &i -> const int*
                  // 2do argumento: 'c' -> int es mejor que 'c' -> short
                  // llama a Fcn(int*, int)
    Fcn(&i, s);   // 1er argumento: &i -> int* es mejor que &i -> const int*
                  // 2do argumento: s -> short es mejor que s -> int
                  // no hay ganador, error de compilación
}

Si la mejor función viable se resuelve en una función para la cual se encontraron múltiples declaraciones, y si dos cualesquiera de estas declaraciones habitan diferentes ámbitos y especifican un argumento por defecto que hizo viable la función, el programa está mal formado.

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, el argumento predeterminado no se utilizó para viabilidad
    f();  // error: se encontró el argumento predeterminado dos veces
}

Clasificación de secuencias de conversión implícita

Las secuencias de conversión implícita argumento-parámetro consideradas por la resolución de sobrecarga corresponden a conversiones implícitas utilizadas en inicialización por copia (para parámetros no referencia), excepto que al considerar la conversión al parámetro de objeto implícito o al lado izquierdo del operador de asignación, no se consideran las conversiones que crean objetos temporales. Cuando el parámetro es el parámetro de objeto implícito de una función miembro static, la secuencia de conversión implícita es una secuencia de conversión estándar que no es ni mejor ni peor que cualquier otra secuencia de conversión estándar. (since C++23)

A cada tipo de secuencia de conversión estándar se le asigna uno de tres rangos:

1) Coincidencia exacta : no se requiere conversión, conversión de lvalue a rvalue, conversión de calificación, conversión de puntero a función, (desde C++17) conversión definida por el usuario de tipo clase a la misma clase
2) Promoción : promoción integral, promoción de punto flotante
3) Conversión : conversión integral, conversión de punto flotante, conversión flotante-integral, conversión de puntero, conversión de puntero a miembro, conversión booleana, conversión definida por el usuario de una clase derivada a su base

El rango de la secuencia de conversión estándar es el peor de los rangos de las conversiones estándar que contiene (puede haber hasta tres conversiones )

La vinculación de un parámetro de referencia directamente a la expresión del argumento es una identidad o una conversión de derivada a base:

struct Base {};
struct Derived : Base {} d;
int f(Base&);    // sobrecarga #1
int f(Derived&); // sobrecarga #2
int i = f(d); // d -> Derived& tiene rango Coincidencia Exacta
              // d -> Base& tiene rango Conversión
              // llama a f(Derived&)

Dado que la clasificación de secuencias de conversión opera únicamente con tipos y categorías de valor, un campo de bits puede vincularse a un argumento de referencia para propósitos de clasificación, pero si esa función resulta seleccionada, será incorrecta en su formación.

1) Una secuencia de conversión estándar es siempre mejor que una secuencia de conversión definida por el usuario o una secuencia de conversión de elipsis.
2) Una secuencia de conversión definida por el usuario es siempre mejor que una secuencia de conversión ellipsis .
3) Una secuencia de conversión estándar S1 es mejor que una secuencia de conversión estándar S2 si
a) S1 es una subsecuencia propia de S2 , excluyendo transformaciones de lvalue; la secuencia de conversión de identidad se considera una subsecuencia de cualquier conversión no identidad, o, si no es eso,
b) el rango de S1 es mejor que el rango de S2 , o, si no es así,
c) ambos S1 y S2 se están vinculando a un parámetro de referencia que no es el parámetro de objeto implícito de una función miembro calificada con ref, y S1 vincula una referencia a valor R a un valor R mientras que S2 vincula una referencia a valor L a un valor R:
int i;
int f1();
int g(const int&);  // sobrecarga #1
int g(const int&&); // sobrecarga #2
int j = g(i);    // valor L int -> const int& es la única conversión válida
int k = g(f1()); // valor R int -> const int&& mejor que valor R int -> const int&
o, si no es eso,
d) ambos S1 y S2 se están enlazando a un parámetro de referencia y S1 enlaza una referencia de lvalue a función mientras que S2 enlaza una referencia de rvalue a función:
int f(void(&)());  // overload #1
int f(void(&&)()); // overload #2
void g();
int i1 = f(g); // calls #1
o, si no es eso,
e) S1 y S2 solo difieren en conversión de calificación, y

la calificación cv del resultado de S1 es un subconjunto propio de la calificación cv del resultado de S2 , y S1 no es la conversión obsoleta de array a puntero de literal de cadena (hasta C++11) .

(hasta C++20)

el resultado de S1 puede convertirse al resultado de S2 mediante una conversión de calificación.

(desde C++20)
int f(const int*);
int f(int*);
int i;
int j = f(&i); // &i -> int* es mejor que &i -> const int*, llama a f(int*)
o, si no es eso,
f) ambos S1 y S2 se enlazan a parámetros de referencia que solo difieren en calificación cv de nivel superior, y el tipo de S1 está menos calificado cv que el de S2 :
int f(const int &); // sobrecarga #1
int f(int &);       // sobrecarga #2 (ambas referencias)
int g(const int &); // sobrecarga #1
int g(int);         // sobrecarga #2
int i;
int j = f(i); // lvalue i -> int& es mejor que lvalue int -> const int&
              // llama a f(int&)
int k = g(i); // lvalue i -> const int& clasifica como Coincidencia Exacta
              // lvalue i -> rvalue int clasifica como Coincidencia Exacta
              // sobrecarga ambigua: error de compilación
o, si no es eso,
g) S1 y S2 enlazan el mismo tipo de referencia "referencia a T " y tienen tipos de origen V1 y V2 , respectivamente, donde la secuencia de conversión estándar de V1 * a T * es mejor que la secuencia de conversión estándar de V2 * a T * :
struct Z {};
struct A
{
    operator Z&();
    operator const Z&();  // overload #1
};
struct B
{
    operator Z();
    operator const Z&&(); // overload #2
};
const Z& r1 = A();        // OK, uses #1
const Z&& r2 = B();       // OK, uses #2
4) Una secuencia de conversión definida por el usuario U1 es mejor que una secuencia de conversión definida por el usuario U2 si ambas llaman al mismo constructor/función de conversión definida por el usuario o inicializan la misma clase con inicialización de agregado, y en cualquier caso la segunda secuencia de conversión estándar en U1 es mejor que la segunda secuencia de conversión estándar en U2
struct A
{
    operator short(); // user-defined conversion function
} a;
int f(int);   // overload #1
int f(float); // overload #2
int i = f(a); // A -> short, followed by short -> int (rank Promotion)
              // A -> short, followed by short -> float (rank Conversion)
              // calls f(int)
5) Una secuencia de inicialización de lista L1 es mejor que la secuencia de inicialización de lista L2 si L1 inicializa un parámetro std::initializer_list , mientras que L2 no lo hace.
void f1(int);                                 // #1
void f1(std::initializer_list<long>);         // #2
void g1() { f1({42}); }                       // elige #2
void f2(std::pair<const char*, const char*>); // #3
void f2(std::initializer_list<std::string>);  // #4
void g2() { f2({"foo", "bar"}); }             // elige #4
6) Una secuencia de inicialización de lista L1 es mejor que la secuencia de inicialización de lista L2 si los parámetros correspondientes son referencias a arrays, y L1 se convierte al tipo "array de N1 T", L2 se convierte a "array de N2 T", y N1 es menor que N2.
(since C++11)
(until C++20)
6) Una secuencia de inicialización de lista L1 es mejor que la secuencia de inicialización de lista L2 si los parámetros correspondientes son referencias a arrays, y L1 y L2 se convierten a arrays del mismo tipo de elemento, y ya sea
  • el número de elementos N1 inicializados por L1 es menor que el número de elementos N2 inicializados por L2, o
  • N1 es igual a N2 y L2 se convierte a un array de límite desconocido y L1 no.
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
(since C++20)

Si dos secuencias de conversión son indistinguibles porque tienen el mismo rango, se aplican las siguientes reglas adicionales:

1) La conversión que no involucra puntero a bool o puntero-a-miembro a bool es mejor que la que sí lo hace.
2) La conversión que promueve una enumeración cuyo tipo subyacente está fijo a su tipo subyacente es mejor que una que promueve al tipo subyacente promovido, si los dos tipos son diferentes.
enum num : char { one = '0' };
std::cout << num::one; // '0', not 48
(desde C++11)


3) Una conversión en cualquier dirección entre el tipo de punto flotante FP1 y el tipo de punto flotante FP2 es mejor que una conversión en la misma dirección entre FP1 y el tipo aritmético 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); // llama a f(std::float32_t) en implementaciones donde
              // float y std::float32_t tienen rangos de conversión iguales
int j = f(y); // error: ambiguo, no hay rango de conversión igual
(desde C++23)
4) La conversión que convierte puntero-a-derivada a puntero-a-base es mejor que la conversión de puntero-a-derivada a puntero-a- void , y la conversión de puntero-a-base a void es mejor que puntero-a-derivada a void .
5) Si Mid se deriva (directa o indirectamente) de Base , y Derived se deriva (directa o indirectamente) de Mid
a) Derived * a Mid * es mejor que Derived * a Base *
b) Derived a Mid & o Mid && es mejor que Derived a Base & o Base &&
c) Base :: * a Mid :: * es mejor que Base :: * a Derived :: *
d) Derived a Mid es mejor que Derived a Base
e) Mid * a Base * es mejor que Derived * a Base *
f) Mid a Base & o Base && es mejor que Derived a Base & o Base &&
g) Mid :: * a Derived :: * es mejor que Base :: * a Derived :: *
h) Mid a Base es mejor que Derived a Base

Las secuencias de conversión ambiguas se clasifican como secuencias de conversión definidas por el usuario porque múltiples secuencias de conversión para un argumento solo pueden existir si involucran diferentes conversiones definidas por el usuario:

class B;
class A { A (B&);};         // constructor de conversión
class B { operator A (); }; // función de conversión definida por el usuario
class C { C (B&); };        // constructor de conversión
void f(A) {} // sobrecarga #1
void f(C) {} // sobrecarga #2
B b;
f(b); // B -> A mediante constructor o B -> A mediante función (conversión ambigua)
      // b -> C mediante constructor (conversión definida por el usuario)
      // las conversiones para la sobrecarga #1 y para la sobrecarga #2
      // son indistinguibles; la compilación falla

Secuencia de conversión implícita en la inicialización de lista

En list initialization , el argumento es un braced-init-list , que no es una expresión, por lo que la secuencia de conversión implícita al tipo de parámetro para la resolución de sobrecarga se determina mediante las siguientes reglas especiales:

  • Si el tipo del parámetro es algún agregado X y la lista de inicialización consiste exactamente en un elemento de la misma clase o clase derivada (posiblemente calificada cv), la secuencia de conversión implícita es la requerida para convertir el elemento al tipo del parámetro.
  • De lo contrario, si el tipo del parámetro es una referencia a un arreglo de caracteres y la lista de inicialización tiene un solo elemento que es un literal de cadena de tipo apropiado, la secuencia de conversión implícita es la conversión identidad.
  • De lo contrario, si el tipo del parámetro es std:: initializer_list < X > , y existe una conversión implícita no estrechante de cada elemento de la lista de inicialización a X , la secuencia de conversión implícita para propósitos de resolución de sobrecarga es la peor conversión necesaria. Si la lista de inicialización entre llaves está vacía, la secuencia de conversión es la conversión identidad.
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};     // selecciona #1 (rvalue double -> double: conversión de identidad)
void g(A);
g({"foo", "bar"}); // selecciona #3 (lvalue const char[4] -> std::string: conversión definida por el usuario)
  • De lo contrario, si el tipo del parámetro es "array de N T" (esto solo ocurre para referencias a arrays), la lista de inicialización debe tener N o menos elementos, y la peor conversión implícita necesaria para convertir cada elemento de la lista (o el par vacío de llaves {} si la lista es más corta que N) a T es la que se utiliza.
  • De lo contrario, si el tipo del parámetro es "array de límite desconocido de T" (esto solo ocurre para referencias a arrays), se utiliza la peor conversión implícita necesaria para convertir cada elemento de la lista a T .
(desde C++20)
typedef int IA[3];
void h(const IA&);
void g(int (&&)[]);
h({1, 2, 3}); // conversión identidad int->int
g({1, 2, 3}); // igual que lo anterior desde C++20
  • De lo contrario, si el tipo del parámetro es un tipo de clase no agregado X , la resolución de sobrecarga selecciona el constructor C de X para inicializar desde la lista de inicializadores de argumentos
  • Si C no es un constructor de lista de inicializadores y la lista de inicializadores tiene un único elemento de tipo X posiblemente calificado cv, la secuencia de conversión implícita tiene rango Exact Match. Si la lista de inicializadores tiene un único elemento de tipo derivado de X posiblemente calificado cv, la secuencia de conversión implícita tiene rango Conversion. (nótese la diferencia con los agregados: los agregados se inicializan directamente desde listas de inicialización de un solo elemento antes de considerar aggregate initialization , los no agregados consideran los constructores initializer_list antes que cualquier otro constructor)
  • en caso contrario, la secuencia de conversión implícita es una secuencia de conversión definida por el usuario con la segunda secuencia de conversión estándar siendo una conversión de identidad.

Si múltiples constructores son viables pero ninguno es mejor que los otros, la secuencia de conversión implícita es la secuencia de conversión ambigua.

struct A { A(std::initializer_list<int>); };
void f(A);
struct B { B(int, double); };
void g(B);
g({'a', 'b'});    // llama a g(B(int, double)), conversión definida por el usuario
// g({1.0, 1,0}); // error: double->int es un estrechamiento, no permitido en list-init
void f(B);
// f({'a', 'b'}); // f(A) y f(B) ambas son conversiones definidas por el usuario
  • De lo contrario, si el tipo del parámetro es un agregado que puede inicializarse desde la lista de inicializadores mediante aggregate initialization , la secuencia de conversión implícita es una secuencia de conversión definida por el usuario con la segunda secuencia de conversión estándar siendo una conversión de identidad.
struct A { int m1; double m2; };
void f(A);
f({'a', 'b'}); // llama a f(A(int, double)), conversión definida por el usuario
  • De lo contrario, si el parámetro es una referencia, se aplican las reglas de inicialización de referencias
struct A { int m1; double m2; };
void f(const A&);
f({'a', 'b'}); // se crea un temporal, se llama a f(A(int, double)). Conversión definida por el usuario
  • De lo contrario, si el tipo del parámetro no es una clase y la lista de inicializadores tiene un elemento, la secuencia de conversión implícita es la requerida para convertir el elemento al tipo del parámetro
  • De lo contrario, si el tipo del parámetro no es un tipo de clase y si la lista de inicializadores no tiene elementos, la secuencia de conversión implícita es la conversión de identidad.

Si el argumento es una lista de inicializadores designados y el parámetro no es una referencia, una conversión solo es posible si el parámetro tiene un tipo agregado que puede inicializarse a partir de esa lista de inicializadores según las reglas para aggregate initialization , en cuyo caso la secuencia de conversión implícita es una secuencia de conversión definida por el usuario cuya segunda secuencia de conversión estándar es una conversión de identidad.

Si, después de la resolución de sobrecarga, el orden de declaración de los miembros del agregado no coincide para la sobrecarga seleccionada, la inicialización del parámetro será incorrecta.

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
}


(since C++20)

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 1 C++98 el comportamiento no estaba especificado cuando la misma
función con posiblemente diferentes argumentos por defecto
(de diferentes ámbitos) es seleccionada
el programa está mal
formado en este caso
CWG 83 C++98 la secuencia de conversión desde un literal de cadena
hacia char * era mejor que hacia const char *
a pesar de que la primera está obsoleta
el rango de la conversión obsoleta
se reduce (fue
eliminada en C++11)
CWG 162 C++98 era inválido si el conjunto de sobrecarga denominado F contiene
una función miembro no estática en el caso de &F(args)
solo inválido si la resolución
de sobrecarga selecciona una función
miembro no estática en este caso
CWG 233 C++98 las referencias y punteros se manejaban de forma inconsistente en
la resolución de sobrecarga con conversiones definidas por el usuario
se manejan
consistentemente
CWG 280 C++98 las funciones de llamada suplente no se añadieron al
conjunto de funciones candidatas para funciones de
conversión declaradas en clases base inaccesibles
eliminó la restricción de
accesibilidad, el programa está
mal formado si se selecciona una
función de llamada suplente y la
función de conversión correspondiente
no puede ser llamada
CWG 415 C++98 cuando se selecciona una plantilla de función como
candidata, sus especializaciones se instanciaban
usando deducción de argumentos de plantilla
no ocurrirá ninguna instanciación
en este caso, sus declaraciones
serán sintetizadas
CWG 495 C++98 cuando las conversiones implícitas para argumentos son igualmente
buenas, una función de conversión no plantilla siempre era
mejor que una función de conversión plantilla, incluso si
esta última podría tener una mejor secuencia de conversión estándar
las secuencias de conversión estándar
se comparan
antes que los niveles de especialización
CWG 1307 C++11 la resolución de sobrecarga basada en el tamaño de arrays no estaba especificada un array más corto es
mejor cuando es posible
CWG 1328 C++11 la determinación de las funciones candidatas al
vincular una referencia al resultado de una conversión no estaba clara
aclarado
CWG 1374 C++98 la conversión de calificación se verificaba antes del enlace
de referencia al comparar secuencias de conversión estándar
invertido
CWG 1385 C++11 una función de conversión definida por el usuario no explícita declarada con
un calificador de referencia no tenía una función sustituta correspondiente
tiene una función sustituta
correspondiente
CWG 1467 C++11 se omitió la inicialización de lista del mismo tipo para
agregados y arreglos
inicialización definida
CWG 1601 C++11 conversión de enum a su tipo subyacente
no prefería el tipo subyacente fijo
el tipo fijo es preferido
sobre aquello a lo que promociona
CWG 1608 C++98 el conjunto de miembros candidatos de un operador unario @
cuyo argumento tiene tipo T1 estaba vacío si
T1 es una clase que se está definiendo actualmente
el conjunto es el resultado de
la búsqueda de nombre calificada de
T1::operator@ en este caso
CWG 1687 C++98 cuando un candidato incorporado es seleccionado por la resolución de sobrecarga,
los operandos sufrirían conversión sin restricción
solo convertir operandos de tipo clase,
y deshabilitó la segunda
secuencia de conversión estándar
CWG 2052 C++98 las especializaciones de plantilla de función sintetizadas mal formadas podrían
añadirse al conjunto de candidatos, haciendo que el programa esté mal formado
no se añaden
al conjunto de candidatos
CWG 2076 C++11 la conversión definida por el usuario se aplica al inicializador único
en una lista de inicializadores anidada durante la inicialización de lista
debido a la resolución de CWG issue 1467
no aplicado
CWG 2137 C++11 constructores de lista de inicialización perdidos frente a constructores
de copia al inicializar por lista X desde { X }
los no-agregados consideran
listas de inicialización primero
CWG 2273 C++11 no existía un desempate entre
constructores heredados y no heredados
el constructor no heredado gana
CWG 2673 C++20 se agregaron candidatos incorporados con la misma lista
de parámetros que un candidato no miembro reescrito
a la lista de candidatos incorporados
no agregado
CWG 2712 C++98 cuando se considera un operador de asignación incorporado,
el primer parámetro no podía vincularse a un
temporal, lo cual ya es imposible [1]
se eliminó el
requisito redundante
CWG 2713 C++20 la restricción de conversión respecto a las listas de inicializadores designados
se aplicaba incluso si el parámetro es una referencia
no restringido en este caso
CWG 2789 C++23 el parámetro de objeto explícito fue incluido
al comparar listas de tipos de parámetros
excluido
CWG 2856 C++11 la resolución de sobrecarga para la inicialización por defecto en el contexto
de la inicialización de lista de copia solo consideraba el constructor de conversión
considera todos los constructores
CWG 2919 C++98 el conjunto de candidatos para la inicialización de referencia por conversión
dependía del tipo de destino de la inicialización
depende del tipo de destino
de la conversión
P2468R2 C++20 se añaden candidatos reescritos basados en operator ==
para a ! = b incluso si existe un operator ! = coincidente
no añadido
  1. El tipo del primer parámetro de un operador de asignación incorporado es "referencia de lvalue a T posiblemente calificado con volatile". Las referencias de este tipo no pueden enlazarse a un temporal.

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 12.2 Resolución de sobrecarga [over.match]
  • Estándar C++20 (ISO/IEC 14882:2020):
  • 12.4 Resolución de sobrecarga [over.match]
  • Estándar C++17 (ISO/IEC 14882:2017):
  • 16.3 Resolución de sobrecarga [over.match]
  • Estándar C++14 (ISO/IEC 14882:2014):
  • 13.3 Resolución de sobrecarga [over.match]
  • Estándar C++11 (ISO/IEC 14882:2011):
  • 13.3 Resolución de sobrecarga [over.match]
  • Estándar C++03 (ISO/IEC 14882:2003):
  • 13.3 Resolución de sobrecarga [over.match]

Véase también