Overload resolution
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:
- Construcción del conjunto de candidate functions .
- Reducción del conjunto a solo viable functions .
- 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).
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 deT. 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 cvT. - 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 aTo a una clase derivada deT, * this se utiliza como el argumento objeto implícito. En caso contrario (si this no está en el ámbito o no apunta aT), se utiliza un objeto ficticio de tipoTcomo 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
Tse 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
explicitenTo en una base deT(a menos que esté oculta), cuyos calificadores cv sean iguales o mayores que los calificadores cv deT, 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:
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.
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)
|
4)
candidatos reescritos
:
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
Si un candidato reescrito de
operator
==
es seleccionado por la resolución de sobrecarga para un operador
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
|
(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
explicitdeSy sus clases base (a menos que estén ocultas) aTo clase derivada deTo una referencia a tales. Si esta inicialización por copia es parte de la secuencia de inicialización directa de cvT(inicializando una referencia para vincularse al primer parámetro de un constructor que toma una referencia a cvT), 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
Sy sus clases base (a menos que estén ocultas) que producen el tipoTo un tipo convertible aTmediante 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
Sy sus clases base (a menos que estén ocultas) que producen el tipoTo un tipo convertible aTmediante 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
Sy 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
T2o referencia a rvalue a cv2T2
-
(al convertir a un lvalue) referencia a lvalue a
cv2
-
donde
cv2
T2es compatible con referencia con cv1T.
-
Para la inicialización directa, las funciones de conversión definidas por el usuario explícitas también se consideran si
T2es del mismo tipo queTo puede convertirse al tipoTcon 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
Ty 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
Ty 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
|
(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 :
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:
M
argumentos, la función candidata que tiene exactamente
M
parámetros es viable
M
parámetros, pero tiene un
parámetro de elipsis
, es viable.
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) |
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 por copia de una clase mediante conversión definida por el usuario ,
- inicialización de un tipo no-clase mediante una función de conversión ,
- inicialización mediante función de conversión para enlace directo de referencia ,
- inicialización mediante constructor durante el segundo paso (inicialización directa) de la inicialización por copia de clase,
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
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í,
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) |
F1
es una función no plantilla mientras que
F2
es una especialización de plantilla, o, si no es eso,
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í,
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:
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.
S1
es
mejor
que una secuencia de conversión estándar
S2
si
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,
S1
es mejor que el rango de
S2
, o, si no es así,
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&
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
S1
y
S2
solo difieren en conversión de calificación, y
|
la calificación cv del resultado de
|
(hasta C++20) |
|
el resultado de
|
(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*)
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
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
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)
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
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:
|
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) |
Mid
se deriva (directa o indirectamente) de
Base
, y
Derived
se deriva (directa o indirectamente) de
Mid
Derived
a
Mid
&
o
Mid
&&
es mejor que
Derived
a
Base
&
o
Base
&&
Derived
a
Mid
es mejor que
Derived
a
Base
Mid
a
Base
&
o
Base
&&
es mejor que
Derived
a
Base
&
o
Base
&&
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
Xy 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) aTes la que se utiliza.
|
(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 |
-
↑
El tipo del primer parámetro de un operador de asignación incorporado es "referencia de lvalue a
Tposiblemente 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]