Resolución de sobrecarga
Para poder compilar una llamada a función, el compilador debe primero ejecutar una búsqueda de nombre, que, para funciones, puede involucrar la búsqueda dependiente de argumento, y para plantillas de función puede estar seguido de la deducción de argumentos de plantilla. Si estos pasos producen más de una función candidato, entonces se ejecuta la resolución de sobrecarga para seleccionar la función que realmente se llamará.
En general, la función candidato cuyos parámetros coincidan más cercanamente con los argumentos es la que se llamará.
Para otros contextos donde pueden aparecer los nombres de funciones sobrecargadas, véase dirección de una función sobrecargada.
Si no se puede seleccionar una función mediante la resolución de sobrecarga (p. ej., es una entidad emplantillada con una restricción fallida), no puede nombrarse o usarse de ninguna forma.
[editar] Detalles
Antes que comience la resolución de sobrecarga, las funciones seleccionadas por búsqueda de nombre y deducción de argumento de plantilla se combinan para formar el conjunto de funciones candidato (los criterios exactos dependen del contexto en el que tiene lugar la resolución de sobrecarga; véase más abajo).
Si alguna función candidato es una función miembro (estática o no estática), pero no un constructor, se trata como si tuviera un parámetro adicional (parámetro objeto implícito) que representa el objeto para el que se llaman y aparece antes del primero de los parámetros reales.
De manera similar, el objeto en el que se llama a una función miembro se antepone a la lista de argumentos como el argumento objeto implícito.
Para las funciones miembro de la clase X
, el tipo del parámetro objeto implícito se ve afectado por las calificaciones-cv y las calificaciones-ref de la función miembro como se describe en funciones miembro.
Las funciones de conversión definidas por el usuario se consideran miembros del argumento objeto implícito con el fin de determinar el tipo del parámetro objeto implícito.
Las funciones miembro introducidas por una declaración using
en una clase derivada se consideran miembros de la clase derivada con el fin de definir el tipo del parámetro objeto implícito.
Para las funciones miembro estáticas, se considera que el parámetro objeto implícito coincide con cualquier objeto: su tipo no se examina y no se intenta ninguna secuencia de conversión para él.
Para el resto de la resolución de sobrecarga, el argumento objeto implícito es indistinguible de otros argumentos, pero las siguientes reglas especiales se aplican al parámetro objeto implícito:
struct B { void f(int); }; struct A { operator B&(); }; A a; a.B::f(1); // ERROR: no se pueden aplicar conversiones definidas por el usuario // al parámetro objeto implícito static_cast<B&>(a).f(1); // de acuerdo
Si algún candidato es una plantilla de función, sus especializaciones se generan utilizando la deducción de argumento de plantilla, y dichas especializaciones se tratan como funciones que no son de plantilla, excepto donde se especifique lo contrario en las reglas de desempate. Si un nombre se refiere a una o más plantillas de función y también a un conjunto de funciones que no son de plantilla sobrecargadas, esas funciones y las especializaciones generadas a partir de las plantillas son todas candidato.
Si una plantilla de constructor o plantilla de función de conversión tiene un especificador |
(desde C++20) |
El constructor de movimiento y la asignación de movimiento por defecto (defaulted) que se definen como eliminados (=delete
) nunca se incluyen en la lista de funciones candidato.
Los constructores heredados de copia y movimiento no se incluyen en la lista de funciones candidato al construir un objeto de clase derivado.
[editar] Funciones candidato
El conjunto de funciones candidato y la lista de argumentos se prepara de una manera única para cada uno de los contextos donde de usa la resolución de sobrecarga:
[editar] Llamada a una función denominada
Si E
en una expresión de llamada a función E(args)
denomina 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 formaPA->B
oA.B
(donde A tiene un tipo clase cv T), entoncesB
es buscada como una función miembro deT
. Las declaraciones de función encontradas por esa búsqueda son las funciones candidato. La lista de argumentos para la resolución de sobrecarga tiene el argumento de objeto implícito de tipo cv T. - Si la expresión
E
es una expresión primaria, el nombre es buscado siguiendo las reglas normales para llamadas a función (que pueden involucrar ADL). Las declaraciones de función encontradas por esta búsqueda son (debido a la forma en que funciona la búsqueda) o bien:
- a) todas las funciones que no son miembros (en cuyo caso la lista de argumentos con el propósito de la resolución de sobrecarga es exactamente la lista de argumentos utilizada en la expresión de llamada de función), o
- b) todas las funciones miembro de alguna clase
T
, en cuyo caso, si this está dentro del ámbito y es un puntero aT
o a una clase derivada deT
,*this
se utiliza como el argumento objeto implícito. De lo contrario (sithis
no está dentro del ámbito o no apunta aT
), se usa un objeto falso de tipoT
como el 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.
[editar] Llamada a un objeto clase
Si E
en una expresión de llamada a función E(args)
tiene un tipo clase cv T
, entonces
- Los operadores de llamada a función de T se obtienen mediante la búsqueda ordinaria del nombre operator() en el contexto de la expresión
(E).operator()
, y cada declaración encontrada se agrega al conjunto de funciones candidato. - Para cada función de conversión definida por el usuario no explícita en
T
o en una base deT
(a menos que esté oculta), cuyos calificadores-cv son los mismos o mayores que los calificadores-cv deT
, y donde la función de conversión convierte a:
- puntero a función
- referencia a puntero a función
- referencia a función
- luego 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, se agrega al conjunto de funciones candidato. Si esta función sustituta se selecciona mediante la resolución de sobrecarga subsiguiente, 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 con el propósito de la resolución de sobrecarga es la lista de argumentos de la expresión de llamada a función precedida por el objeto argumento implícito E
(cuando se compara con la función sustituta, la conversión definida por el usuario automáticamente convertirá el objeto argumento 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 de puntero a función using fp2 = int(*)(float); operator fp2() { return f2; } // función de conversión de puntero a función } a; int i = a(1); // llama a f1 vía puntero devuelto desde función de conversión
[editar] Llamada a un operador sobrecargado
Si al menos uno de los argumentos de un operador en una expresión tiene un tipo clase o un tipo enumeración, tanto los operadores integrados como las sobrecargas de operadores definidas por el usuario participan en resolución de sobrecarga, con el conjunto de funciones candidato seleccionadas de la siguiente manera:
Para un operador unario @
cuyo argumento tiene el tipo T1
(después de eliminar las calificaciones-cv), o el operador binario @
cuyo operando izquierdo tiene el tipo T1
y el operando derecho de tipo T2
(después de eliminar las calificaciones-cv), se preparan los siguientes conjuntos de funciones candidato:
T1
es una clase completa o una clase que se está definiendo actualmente, el conjunto de candidatos miembro es el resultado de la búsqueda de nombre calificado de T1::operator@
. En todos los demás casos, el conjunto de candidatos miembro está vacío.operator@
en el contexto de la expresión (que puede involucrar ADL), excepto que las declaraciones de función miembro se ignoran y no evitan que la búsqueda continúe en el siguiente ámbito adjunto. Si ambos operandos de un operador binario o el único operando de un operador unario tiene un tipo enumeración, las únicas funciones del conjunto de búsqueda que se convierten en candidatos no miembro son aquellas cuyo parámetro tiene ese tipo de enumeración (o referencia a ese tipo de 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 candidato que se enviarán para la resolución de sobrecarga es una unión de los conjuntos anteriores. La lista de argumentos con el propósito de la resolución de sobrecarga consiste en los operandos del operador excepto para operator->
, donde el segundo operando no es un argumento para la llamada a función (véase operador de acceso de 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 integrados: int(a) + int(b) // la resolución de sobrecarga escoge a operator+(a,b) }
Si la resolución de sobrecarga selecciona un candidato integrado, la secuencia de conversión definida por el usuario de 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 hay operator+ entre puntero y double
Para el operador operator,, el operador unario operator&, y el operador operator->, si no existen funciones viables (véase más abajo) en el conjunto de funciones candidato, entonces el operador se reinterpreta como un operador integrado.
Si un operador candidato operator<=> reescrito se selecciona por la resolución de sobrecarga para un operador Si un operador candidato operator== reescrito se selecciona 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 a candidatos reescritos, y prefiere candidatos reescritos no sintetizados a candidatos reescritos sintetizados. Esta búsqueda con el orden inverso de los argumentos 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 ambos sentidos. Véase comparaciones por defecto para obtener más detalles. |
(desde C++20) |
[editar] Inicialización por constructor
Cuando un objeto de tipo clase es inicializado mediante la inicialización directa o la inicialización por defecto fuera de un contexto de inicialización de copia, las funciones candidato son todas constructores de la clase que se está inicializando. La lista de argumentos es la lista de expresiones del inicializador.
Cuando un objeto de tipo clase se inicializa mediante la inicialización de copia a partir de un objeto del mismo tipo de clase o derivado, o se inicializa por defecto en un contexto de inicialización de copia, las funciones candidato son todas constructores de conversión de la clase que se está inicializado. La lista de argumentos es la expresión del inicializador.
[editar] Inicialización de copia por conversión
Si la inicialización de copia de un objeto de tipo clase requiere que se llame a una función de conversión definida por el usuario para convertir la expresión inicializadora de tipo cv S
al tipo cv T
del objeto que se inicializa, las siguientes funciones son funciones candidato:
- todos los constructores de conversión de
T
, y - las funciones de conversión no explícitas de
S
y sus clases base (a menos que estén ocultas) aT
o a una clase derivada deT
o una referencia a tal. Si esta inicialización de copia es parte de la secuencia de inicialización directa de cvT
(inicializando una referencia para ser vinculada al primer parámetro de un constructor que toma una referencia a cvT
), también se consideran las funciones de conversión explícitas.
De cualquier manera, la lista de argumentos con el propósito de la resolución de sobrecarga consiste en un solo argumento que es la expresión inicializadora, que se comparará con el primer argumento del constructor o con el argumento objeto argumento implícito de la función de conversión.
[editar] Inicialización de no-clases por conversión
Cuando la inicialización de un objeto de tipo que no es de clase cv1 T
requiere una función de conversión definida por el usuario para convertir de una expresión inicializadora de tipo clase cv S
, las siguientes funciones son candidato:
- las funciones de conversión no explícitas definidas por el usuario de
S
y sus clases base (a menos que estén ocultas) que producen el tipoT
o un tipo convertible aT
mediante una secuencia de conversión estándar, o una referencia a dicho tipo. Los calificadores cv en el tipo devuelto se ignoran con el fin de seleccionar funciones candidato; - si se trata de inicialización directa, 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 tipoT
o un tipo convertible aT
mediante una conversión de calificación, o una referencia a dicho tipo, también se consideran.
De cualquier manera, la lista de argumentos con el propósito de la resolución de sobrecarga consiste en un solo argumento que es la expresión inicializadora, que se comparará con el objeto argumento implícito de la función de conversión.
[editar] Inicialización de referencia por conversión
Durante la inicialización de referencia, donde la referencia a cv1 T
está vinculada al resultado l-valor o r-valor de una conversión de la expresión inicializadora del tipo clase cv2 S
, se seleccionan las siguientes funciones para el conjunto de candidatos:
- las funciones de conversión no explícitas definidas por el usuario de
S
y sus clases base (a menos que estén ocultas) al tipo
- * (al inicializar una referencia l-valor o una referencia r-valor a función) referencia l-valor a cv2
T2
, - * (al inicializar una referencia r-valor o una referencia l-valor a función) cv2
T2
o la referencia r-valor a cv2T2
. - donde cv2 T2 es compatible-con-referencia con cv1 T,
- para la inicialización directa, las funciones de conversión explícitas definidas por el usuario también se consideran si T2 es del mismo tipo que T o se puede convertir al tipo T con una conversión de calificación.
De cualquier manera, la lista de argumentos con el propósito de la resolución de sobrecarga consiste en un solo argumento que es la expresión inicializadora, que se comparará con el objeto argumento implícito de la función de conversión.
[editar] Inicialización de lista
Cuando un objeto de tipo clase que no es un agregado T
es inicializado mediante la inicialización de lista, se produce una resolución de sobrecarga de dos fases:
- en la fase 1, las funciones candidato son todos constructores de lista de inicializadores de
T
y la lista de argumentos con el propósito de la resolución de sobrecarga consiste en un solo argumento de lista de inicializadores, - si la resolución de sobrecarga falla en la fase 1, se ingresa a la fase 2, donde las funciones candidato son todas constructores de
T
y la lista de argumentos con el propósito de la resolución de sobrecarga consiste en los elementos individuales de la lista de inicializadores.
Si la lista de inicializadores está vacía y T
tiene un constructor por defecto, 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 inicializaciones de copia donde los constructores explícitos ni siquiera se consideran).
[editar] Funciones viables
Dado el conjunto de funciones candidato, construido como se describió anteriormente, el siguiente paso de la resolución de sobrecarga es examinar argumentos y parámetros para reducir el conjunto el conjunto de funciones viables
Para ser incluida en el conjunto de funciones viables, la función candidato debe satisfacer lo siguiente:
M
argumentos, la función candidato que tiene exactamente M
parámetros es viableM
parámetros, pero tiene un parámetro de elipsis, es viable.M
parámetros y el parámetro M + 1
-ésimo y todos los parámetros que siguen 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 asociada una restricción, debe cumplirse.
|
(desde C++20) |
Se prohíbe que las conversiones definidas por el usuario (tanto los constructores de conversión como las funciones de conversión definidas por el usuario) participen en la secuencia de conversión implícita en la que sería posible aplicar más de una conversión definida por el usuario. Específicamente, no se consideran si el objetivo de la conversión es el primer parámetro de un constructor o el objeto parámetro implícito de una función de conversión definida por el usuario, y ese constructor o conversión definida por el usuario es un candidato para:
- inicialización de copia de una clase por conversión definida por el usuario,
- inicialización de un tipo que no es una clase por una función de conversión,
- inicialización por una función de conversión para una vinculación de referencia directa,
- inicialización por constructor durante el segundo (inicialización directa) paso de la inicialización de copia de la clase,
- inicialización por inicialización de lista donde la lista de inicializadores tiene exactamente un elemento que es en sí mismo una lista de inicializadores, y el destino es el primer parámetro de un constructor de clase X, y la conversión es a X o referencia a (posiblemente calificado-cv) X
struct A { A(int); }; struct B { B(A); }; B b{ {0} }; // inicialización de lista de B // candidates: B(const B&), B(B&&), B(A) // {0} -> B&& no es viable: tendría que llamar a B(A) // {0} -> const B&: no es viable: tendría que vincularse a un r-valor, tendría que llamar a B(A) // {0} -> A es viable. Llama a A(int): conversión definida por el usuario a A no está prohibida
[editar] La mejor función viable
Para cada par de funciones viables F1
y F2
, las secuencias de conversión implícitas del i
-ésimo argumento al i
-ésimo parámetro son clasificadas para determinar cuál es mejor (excepto el primer argumento, el objeto argumento implícito para funciones miembro estáticas no tiene ningún efecto en la clasificación).
Se determina que F1
es una función mejor que F2
si las conversiones implícitas para todos los argumentos de F1 "no son peores" que las conversiones implícitas para todos los argumentos de F2, y
3) o, si no es así, (solo en el contexto de inicialización por función de conversión para el vínculo de referencia directo de una referencia a tipo función), el tipo de retorno de F1 es el mismo tipo de referencia (l-valor o r-valor) que la referencia que está siendo inicializada, y el tipo de retorno de F2 no es,
|
(desde C++11) |
6) o, si no es así, F1 y F2 son funciones que no son de plantilla con las mismas listas de tipos de parámetros, y F1 es más restringida que F2 según la ordenación parcial de restricciones,
|
(desde C++20) |
7) o, si no es así, 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,
|
(desde C++11) |
8) o, si no es así, F2 es un candidato reescrito y F1 no,
9) o, si no es así, F1 y F2 son ambas candidatos reescritos, y F2 es un candidato sintetizado reescrito con el orden de los parámetros invertido y F1 no,
|
(desde C++20) |
10) o, si no es así, F1 se genera de una guía de deducción definida por el usuario y F2 no,
11) o, si no es así, F1 es el candidato de deducción de copia y F2 no,
12) o, si no es así, F1 se genera de un constructor que no es de plantilla y F2 se genera de un constructor de plantilla.
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 de copia A x (1, 2, 3); // usa #3, generada de un constructor que no es de 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>> and #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 se ejecuta correctamente y se llama a esta función. 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 and 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 }
[editar] Clasificación de secuencias de conversión implícitas
Las secuencias de conversión implícitas de parámetro-argumento consideradas por la resolución de sobrecarga corresponden a las conversiones implícitas utilizadas en la inicialización de copia (para parámetros que no son referencias), excepto que cuando se considera la conversión al parámetro objeto implícito o al lado izquierdo del operador de asignación, no se consideran las conversiones que crean objetos temporales.
A cada tipo de secuencia de conversión estándar se le asigna una de tres clasificaciones:
La clasificación de la secuencia de conversión estándar es la peor de las clasificaciones de las conversiones estándar que tiene (puede haber hasta tres conversiones).
La vinculación de un parámetro de referencia directamente a la expresión del argumento es ya sea una conversión de identidad de clase derivada a clase base:
struct Base {}; struct Derivada : Base {} d; int f(Base&); // sobrecarga #1 int f(Derivada&); // sobrecarga #2 int i = f(d); // d -> Derivada& tiene una clasificación de Coincidencia exacta // d -> Base& tiene una clasificación de Conversión // llama a f(Derivada&)
Dado que la clasificación de las secuencias de conversión opera solo con tipos y categorías de valor, un campo de bits se puede vincular a un argumento de referencia con el fin de clasificar, pero si se selecciona esa función, estará mal formada.
S1
es mejor que una secuencia de conversión estándar S2
siS1
es una subsecuencia de S2
, excluyendo las transformaciones de l-valor. La secuencia de conversión de identidad se considera una subsecuencia de cualquier otra conversión.S1
es mejor que la clasificación de S2
.S1
como S2
se vinculan a un parámetro de referencia a algo diferente al parámetro de objeto implícito de una función miembro calificada por referencia (&), y S1
se vincula a una referencia r-valor a un r-valor mientras que S2
enlaza una referencia l-valor a un r-valor.
int i; int f1(); int g(const int&); // sobrecarga #1 int g(const int&&); // sobrecarga #2 int j = g(i); // l-valor int -> const int& es la única conversión válida int k = g(f1()); // r-valor int -> const int&& mejor que r-valor int -> const int&
S1
como S2
se vinculan a un parámetro de referencia y S1
se vincula a una referencia l-valor a función mientras que S2
se vincula a una referencia r-valor a función.
int f(void(&)()); // sobrecarga #1 int f(void(&&)()); // sobrecarga #2 void g(); int i1 = f(g); // llama a #1
S1
como S2
se vinculan a parámetros de referencia que solo son distintos en la calificación-cv de nivel superior, y el tipo de S1
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); // l-valor i -> int& es mejor que l-valor int -> const int& // llama f(int&) int k = g(i); // l-valor i -> const int& se clasifica Coincidencia exacta // l-valor i -> rvalue int se clasifica Coincidencia exacta // sobrecarga ambigua: error de compilación
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*)
U1
es mejor que una secuencia de conversión definida por el usuario U2
si llaman al mismo constructor o 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(); // función de conversión definida por el usuario } a; int f(int); // sobrecarga #1 int f(float); // sobrecarga #2 int i = f(a); // A -> short, seguido de short -> int (clasificación de Promoción) // A -> short, seguido de short -> float (clasificación de Conversión) // llama a f(int)
L1
es mejor que una secuencia de inicialización de lista L2
si L1
inicializa un parámetro de tipo std::initializer_list mientras que L2
no.
void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // escoge #2 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo","bar"}); } // escoge #4
6) Una secuencia de inicialización de lista L1 es mejor que una secuencia de inicialización de lista L2 y los parámetros correspondientes son referencias a arrays, y L1 se convierte al tipo "array de N1 T," L2 se convierte al tipo "array de N2 T", y N1 es más pequeña que N2.
|
(desde C++11) (hasta C++20) |
6) Una secuencia de inicialización de lista L1 es mejor que una secuencia de inicialización de lista L2 y los parámetros correspondientes son referencias a arrays, y L1 y L2 se convierte a arrays del mismo tipo de elemento, y o bien
void f(int (&&)[] ); // sobrecarga #1 void f(double (&&)[] ); // sobrecarga #2 void f(int (&&)[2]); // sobrecarga #3 f({1}); // #1: Mejor que #2 debido a conversión, mejor que #3 debido a vínculos f({1.0}); // #2: double -> double es mejor que double -> int f({1.0, 2.0}); // #2: double -> double es mejor que double -> int f({1, 2}); // #3: -> int[2] es mejor que -> int[], // e int -> int es mejor que int -> double |
(desde C++20) |
2) Una 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', no 48 |
(desde C++11) |
Intermedia
se deriva (directamente o indirectamente) de Base
, y Derivada
se deriva (directamente o indirectamente) de Intermedia
Las secuencias de conversión ambiguas se clasifican como secuencias de conversión definidas por el usuario porque solo pueden existir varias secuencias de conversión para un argumento si implican 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 vía ctor o B -> A vía función (conversión ambigua) // b -> C vía ctor (conversión definida por el usuario) // las conversiones para la sobrecarga #1 y para la sobrecarga #2 // son indistinguibles; la compilación falla
[editar] Secuencia de conversión implícita en la inicialización de lista
En la inicialización de lista, El argumento es una lista-de-inicializadores-entre-llaves, que no es una expresión, por lo que la secuencia de conversión implícita en el tipo de parámetro con el propósito de la resolución de sobrecarga se decide mediante las siguientes reglas especiales:
- Si el tipo de parámetro es algún agregado
X
y la lista de inicializadores consta exactamente de un elemento de la misma clase o de una clase derivada (posiblemente calificado-cv), la secuencia de conversión implícita es la que se requiere para convertir el elemento al tipo de parámetro. - De lo contrario, si el tipo de parámetro es una referencia al array de caracteres y la lista de inicializadores tiene un solo elemento que es un literal de cadena con el tipo apropiado, la secuencia de conversión implícita es la conversión de identidad.
- De lo contrario, si el tipo de parámetro es std::initializer_list<X>, y hay una conversión implícita no estrechante de cada elemento de la lista de inicializadores a
X
, la secuencia de conversión implícita para el propósito de la resolución de sobrecarga es la peor conversión necesaria. Si la lista de inicializadores entre llaves está vacía, la secuencia de conversión es la conversión de 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 (r-valor double -> double: conv. de identidad) void g(A); g({"foo","bar"}); // selecciona #3 (l-valor const char[4] -> std::string: conv. def. por usuario)
- De lo contrario, si el tipo de parámetro es "array de
N
T
" (esto solo ocurre para referencias a arrays), la lista de inicializadores debe tenerN
o menos elementos, y la peor conversión implícita necesaria para convertir todos los elementos de la lista (o el par vacío de llaves {} si la lista es más corta queN
) aT
es la que se usa.
|
(desde C++20) |
typedef int IA[3]; void h(const IA&); void g(int (&&)[]); h({1,2,3}); // int->int conversión de identidad g({1,2,3}); // igual que arriba desde C++20
- De lo contrario, si el tipo de parámetro es un tipo clase no-agregado
X
, la resolución de sobrecarga elige el constructorC
deX
para inicializar a partir de la lista de inicializadores argumento.
- Si
C
no es un constructor de lista de inicializadores y la lista de inicializadores tiene un solo elemento X posiblemente calificado-cv, la secuencia de conversión implícita tiene una clasificación de Coincidencia exacta. Si la lista de inicializadores tiene un solo elemento de tipo posiblemente calificado-cv derivado deX
, la secuencia de conversión implícita tiene una clasificación de Conversión (observa la diferencia con los agregados: los agregados se inicializan directamente desde las listas de inicializadores de un solo elemento antes de considerar la inicialización de agregado, los no-agregados consideran los constructores que toman una lista de inicializadores antes que cualquier otro constructor). - De lo contrario, la secuencia de conversión implícita es una secuencia de conversión definida por el usuario donde la segunda secuencia de conversión estándar es una conversión de identidad.
- Si
Si varios constructores son viables pero ninguno es mejor que los demás, 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 estrechante, // no se admite en la inicialización de lista void f(B); // f({'a','b'}); // tanto f(A) como f(B) conversión definida por el usuario
- De lo contrario, si el tipo de parámetro es un agregado que se puede inicializar desde la lista de inicializadores según la inicialización de agregado, la secuencia de conversión implícita es una secuencia de conversión definida por el usuario donde la segunda secuencia de conversión estándar es 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 referencia.
struct A { int m1; double m2; }; void f(const A&); f({'a','b'}); // temporal creado, se llamó a f(A(int,double)),conversión definida por el usuario
- De lo contrario, si el tipo de parámetro no es tipo clase y la lista de inicializadores tiene un elemento, la secuencia de conversión implícita es la que se requiere para convertir el elemento al tipo de parámetro.
- De lo contrario, si el tipo de parámetro no es un tipo 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 designada, una conversión solo es posible si el parámetro tiene un tipo agregado que se puede inicializar desde esa lista de inicializadores de acuerdo con las reglas para la inicialización de agregado, 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 la sobrecarga, el orden de declaración de los miembros del agregado no coincide con la sobrecarga seleccionada, la inicialización del parámetro estará mal formada. 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); // de acuerdo; llama a #1 f({.y = 2, .x = 1}, 0); // ERROR: selecciona #1, la inicialización de a // falla debido a un orden de miembros que no coincide g({.x = 1, .y = 2}); // ERROR: ambigua entre #3 y #4 }
|
(desde C++20) |
[editar] Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
CWG 1 | C++98 | El comportamiento no estaba especificado cuando se seleccionaba la misma función, con posiblemente distintos argumentos por defecto (de distintos ámbitos). |
En este caso el programa está mal formado. |
CWG 83 | C++98 | La secuencia de conversión de un literal de cadena a char* era mejor que la de const char* a pesar de que la primera está en desuso. |
Se disminuye la clasificación de la conversión puesta en desuso (se eliminó la conversión implícita en C++11). |
CWG 1307 | C++11 | No se especificaba la resolución de sobrecarga basada en el tamño de los arrays. | Un array más corto es mejor cuando sea posible. |
CWG 1467 | C++11 | Se omitía la inicialización por lista de agregados del mismo tipo y arrays. | Se definió la inicialización. |
CWG 1601 | C++11 | La conversión de enum a su tipo subyacente no prefería el tipo subyacente fijo. |
Se prefiere el tipo fijo al que se promueve. |
CWG 2137 | C++11 | Los constructores de inicialización por lista perdían frente a los constructores de copia al inicializar por lista a X de {{{X}}}. |
Los no agregados consideran primero las inicializaciones por lista. |
[editar] Referencias
- El estándar C++20 (ISO/IEC 14882:2020):
- 12.4 Overload resolution [over.match]
- El estándar C++17 (ISO/IEC 14882:2017):
- 16.3 Overload resolution [over.match]
- El estándar C++14 (ISO/IEC 14882:2014):
- 13.3 Overload resolution [over.match]
- El estándar C++11 (ISO/IEC 14882:2011):
- 13.3 Overload resolution [over.match]
- El estándar C++03 (ISO/IEC 14882:2003):
- 13.3 Overload resolution [over.match]