Espacios de nombres
Variantes
Acciones

Resolución de sobrecarga

De cppreference.com
< cpp‎ | language

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.

Contenido

[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:

1) las conversiones definidas por el usuario no se pueden aplicar al parámetro objeto implícito;
2) los r-valores ​​se pueden vincular al parámetro objeto implícito no constante (a menos que esto sea para una función miembro calificada-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: 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 explicit condicional que resulta ser dependiente del valor, después de la deducción, si el contexto requiere un candidato que no es explícito y la especialización generada es explícita, se elimina del conjunto de candidatos.

(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 forma PA->B o A.B (donde A tiene un tipo clase cv T), entonces B es buscada como una función miembro de T. 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 a T o a una clase derivada de T, *this se utiliza como el argumento objeto implícito. De lo contrario (si this no está dentro del ámbito o no apunta a T), se usa un objeto falso de tipo T 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 de T (a menos que esté oculta), cuyos calificadores-cv son los mismos o mayores que los calificadores-cv de T, 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:

1) candidatos miembro: si 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.
2) candidatos no miembro: Para los operadores donde sobrecarga de operadores permite formas no miembro, todas las declaraciones encontradas por la búsqueda de nombre sin calificar de 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)
3) candidatos integrados : Para el operador operator,, el operador unario operator& y el operador operator->, el conjunto de candidatos está vacío. Para otros operadores, los candidatos integrados son los que se enumeran en las páginas de operadores integrados siempre que todos los operandos se puedan convertir implícitamente a sus parámetros. Si algún candidato integrado tiene la misma lista de parámetros que un candidato no miembro que no es una especialización de plantilla de función, no se agrega a la lista de candidatos integrados. Cuando se consideran los operadores de asignación integrados, las conversiones de sus argumentos de la izquierda están restringidas: no se consideran las conversiones definidas por el usuario.
4) candidatos reescritos:
  • Para las cuatro expresiones de operadores relacionales x<y, x<=y, x>y, y x>=y, todos los operadores operator<=> miembro, no miembro e integrados encontrados se agregan 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 agrega un candidato sintetizado con el orden de los dos parámetros invertido para cada operador operator<=> miembro, no miembro e integrado encontrado.
  • Para x!=y, todos los operadores operator== miembro, no miembro e integrado encontrados se agregan al conjunto.
  • Para las expresiones del operador de igualdad x==y y x!=y, se agrega un candidato sintetizado con el orden de los dos parámetros invertido para cada operador operator== miembro, no miembro e integrado encontrado.

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 @, x @ y se interpreta como la expresión reescrita: 0 @ (y <=> x) si el candidato seleccionado es un candidato sintetizado con el orden de los parámetros invertido, o (x <=> y) @ 0 de lo contrario, usando el candidato reescrito seleccionado operator<=>.

Si un operador candidato operator== reescrito se selecciona por la resolución de sobrecarga para un operador @ (que es ya sea == 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 el orden de los parámetros invertido, o !(x == y) de lo contrario, usando el candidato reescrito seleccionado operator==.

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) a T o a una clase derivada de T o una referencia a tal. Si esta inicialización de copia es parte de la secuencia de inicialización directa de cv T (inicializando una referencia para ser vinculada al primer parámetro de un constructor que toma una referencia a cv T), 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 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 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 tipo T o un tipo convertible a T 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 cv2 T2.
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:

1) Si hay M argumentos, la función candidato que tiene exactamente M parámetros es viable
2) Si la función candidato tiene menos de M parámetros, pero tiene un parámetro de elipsis, es viable.
3) Si la función candidato tiene más de 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)
5) Para cada argumento debe haber al menos una secuencia de conversión implícita que lo convierta en el parámetro correspondiente.
6) Si algún parámetro tiene un tipo referencia, el vínculo de referencia se tiene en cuenta en este paso: si un argumento r-valor corresponde a un parámetro de referencia l-valor no constante o un argumento l-valor corresponde a un parámetro de referencia r-valor, la función no es viable.

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:

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

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,
2) o, si no es así, (solo en el contexto de la inicialización de no clases por conversión), la secuencia de conversión estándar del tipo de retorno de F1 al tipo que se está inicializando es mejor que la secuencia de conversión estándar del tipo de retorno de F2,
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)
4) o, si no es así, F1 es una función que no es de plantilla, mientras que F2 es una especialización de plantilla,
5) o, si no es así, F1 y F2 son ambas especializaciones de plantilla y F1 es más especializada según las reglas de ordenación parcial para especializaciones de plantilla,
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:

1) Coincidencia exacta: no se requiere conversión, conversión de l-valor a r-valor, conversión de calificación, conversión de puntero a función, (desde C++17) conversión de tipo clase definida por el usuario a la misma clase.
2) Promoción: promoción de enteros, promoción de punto flotante.
3) Conversión: promoción de enteros, promoción de punto flotante, conversión de punto flotante, 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.

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.

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 puntos suspensivos.
2) Una secuencia de conversión definida por el usuario es siempre mejor que una secuencia de conversión de puntos suspensivos.
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 de S2, excluyendo las transformaciones de l-valor. La secuencia de conversión de identidad se considera una subsecuencia de cualquier otra conversión.
b) O, si no es así, la clasificación de S1 es mejor que la clasificación de S2.
c) O, si no es así, tanto 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&
d) O, si no es así, tanto 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
e) O, si no es así, tanto 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
f) O, si no es así, S1 y S2 solo difieren en la conversión de calificación, y

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

(hasta C++20)

el resultado de S1 puede convertirse al resultado de S2 por 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*)
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 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)
5) Una secuencia de inicialización de lista 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
  • el número de elementos N1 inicializado por L1 es menor que el número de elementos N2 inicializado 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    (&&)[] ); // 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)
Si dos secuencias de conversión son indistinguibles porque tienen la misma clasificación, se aplican las siguientes reglas adicionales:
1) Una conversión que involucra un puntero a bool o puntero a miembro a bool es peor que una que no.
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)
3) Una conversión que convierte de puntero-a-clase-derivada a puntero-a-clase-base es mejor que la conversión de puntero-a-void es mejor que la de puntero-a-clase-derivada a void.
4) Si Intermedia se deriva (directamente o indirectamente) de Base, y Derivada se deriva (directamente o indirectamente) de Intermedia
a) Derivada* a Intermedia* es mejor que Derivada* a Base*;
b) Derivada a Intermedia& o Intermedia&& es mejor que Derivada a Base& o Base&&;
c) Base::* a Intermedia::* es mejor que Base::* a Derivada::*;
d) Derivada a Intermedia es mejor que Derivada a Base;
e) Intermedia* a Base* es mejor que Derivada* a Base*;
f) Intermedia a Base& o Base&& es mejor que Derivada a Base& o Base&&;
g) Intermedia::* a Derivada::* es mejor que Base::* a Derivada::*;
h) Intermedia a Base es mejor que Derivada a Base.

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 tener N 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 que N) a T es la que se usa.
  • De lo contrario, si el tipo de parámetro es "array de límite desconocido de T" (esto solo ocurre para referencias a arrays), la peor conversión implícita necesaria para convertir cada elemento de la lista a T 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 constructor C de X 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 de X, 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 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]

[editar] Véase también