Resolución de sobrecarga y matrices: ¿qué función se debe llamar?
Considere el siguiente programa:
#include <cstddef>
#include <cstdio>
void f(char const*&&) { std::puts("char const*&&"); } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)
template <std::size_t N>
void f(char const (&)[N]) { std::puts("char const(&)[N]"); } // (3)
int main()
{
const char data[] = "a";
f(data);
}
Que f
debe ser llamado? ¿Por qué?
Las últimas versiones publicadas de tres compiladores no están de acuerdo en la respuesta a esta pregunta:
- (1) se llama cuando el programa se compila usando g++ 4.5.2
- (2) se llama cuando el programa se compila usando Visual C++ 2010 SP1
- (3) se llama cuando el programa se compila usando Clang 3.0 (tronco 127530)
¿Han cambiado sustancialmente las reglas de resolución de sobrecarga en diferentes borradores de C++0x? O, ¿dos de estos compiladores están realmente completamente equivocados? ¿Qué sobrecarga es la correcta para ser seleccionada por el último borrador de C++0x?
3 answers
Primero, la secuencia de conversión de los tres es la misma, excepto que para los dos primeros, hay una transformación lvalue (conversión lvalue a rvalue), que sin embargo no se usa para ordenar las secuencias de conversión. Los tres son coincidencias exactas (la especialización de la plantilla de función tiene el tipo de parámetro char const(&)[2]
).
Si repasa las reglas en 13.3.3.2p3
, se detiene en este párrafo
S1 y S2 son enlaces de referencia (8.5.3) y ninguno se refiere a un objeto implícito parámetro de una función miembro no estática declarada sin un calificador ref, y S1 enlaza una referencia rvalue a un rvalue y S2 enlaza una referencia lvalue.
No se puede formar una secuencia de conversión si requiere vincular una referencia rvalue a un lvalue, dice la especificación en 13.3.3.1.4p3. Si observa cómo funciona el enlace de referencia en la última viñeta 8.5.3p5, creará un temporal (creo que significaron rvalue temporal) de tipo char const*
desde el array lvalue y enlazará el referencia temporal. Por lo tanto, creo que (1)
es mejor que (2)
. Lo mismo vale para (1)
contra (3)
, aunque no necesitaríamos esto porque (3)
es una plantilla por lo que en un empate, elegiríamos (1)
de nuevo.
En n3225
, cambiaron las reglas de enlace de referencia para que las referencias rvalue puedan vincularse a expresiones del inicializador que son lvalues, siempre y cuando la referencia esté vinculada a un rvalue (posiblemente creado convirtiendo el inicializador correctamente antes). Esto podría influir el manejo por Visual C++, que puede no estar actualizado aquí.
No estoy seguro de clang. Incluso si se ignoran (1)
, entonces terminaría en un empate entre (2)
y (3)
, y tendría que elegir (2)
porque es un no-plantilla.
Creo que 8.5.3p5 última bala es confuso porque dice "De lo contrario un temporal de tipo ..". No está claro si el temporal es considerado como un lvalue o como un rvalue por 13.3.3.1. 4p3, lo que significa que no estoy seguro de cómo el los siguientes deben comportarse de acuerdo con las palabras exactas de la especificación
void f(int &);
void f(int &&);
int main() {
int n = 0;
f(n);
}
Si asumimos que el temporal es tratado como un rvalue por la cláusula 13, entonces enlazamos un rvalue ref a un rvalue en la segunda función y un lvalue en la primera. Por lo tanto, elegiremos la segunda función y luego obtendremos un diagnóstico por la última bala 8.5.3p5 porque T1
y T2
están relacionados con la referencia. Si asumimos que el temporal es tratado como un lvalue por la cláusula 13, entonces lo siguiente no trabajo
void f(int &&);
int main() {
f(0);
}
Porque enlazaríamos un rvalue ref a un lvalue que por la cláusula 13 hará inviable la función. Y si interpretamos "binding an rvalue ref to an lvalue" para referirnos a la expresión inicializadora en lugar de la expresión final enlazada a, no aceptaremos lo siguiente
void f(float &&);
int main() {
int n = 0;
f(n);
}
Esto sin embargo es válido a partir de n3225. Así que parece haber cierta confusión-envié un DR a la comisión sobre esto.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-18 13:09:29
Afirmo que #3 es la función elegida por un compilador conforme.
(1) es mejor que (2) porque "La secuencia de conversión estándar S1 es una mejor secuencia de conversión que la secuencia de conversión estándar S2 si S1 y S2 son enlaces de referencia (8.5.3) y ninguno se refiere a un parámetro de objeto implícito de una función miembro no estática declarada sin un calificador ref, y S1 enlaza una referencia rvalue a un rvalue y S2 enlaza una referencia lvalue."
(3) es mejor que ambos (1) and (2) because it is an identity conversion (the others are exact match conversions) and "Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence)"
Plantilla vs no plantilla es solo considerado cuando ninguna conversión es mejor " o, si no eso..."
Por extraño que parezca, Comeau prefiere (2) sobre (3). Este caso de prueba falla al compilar:
#include <cstddef>
#include <cstdio>
// (1) removed because Comeau doesn't support rvalue-references yet
char f(char const* const&) { std::puts("char const* const&"); return 0; } // (2)
template <std::size_t N>
int f(char const (&)[N]) { std::puts("char const(&)[N]"); return 0; } // (3)
int main()
{
const char data[] = "a";
switch (0) {
case sizeof(char):
break;
case sizeof(f(data)):
break;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-18 03:32:58
Esta es una respuesta wiki de la comunidad para recopilar fragmentos del estándar (borrador 3225).
Sección 13.3.3 "Mejor función viable" [over.match.best]
-
Defina ICSi(F) como sigue:
-
Si
F
es una función miembro estática, ICS1 (F) se define de tal manera que ICS1 (F) no es ni mejor ni peor que ICS1 (G) para cualquier funciónG
, y, simétricamente, ICS1(G) no es ni mejor ni peor que ICS1(F); de lo contrario, Let ICSi (F) denota la secuencia de conversión implícita que convierte el i-ésimo argumento en la lista a la tipo del i-ésimo parámetro de la función viable
F
. 13.3.3.1 define las secuencias de conversión implícitas y 13.3.3.2 define lo que significa que una secuencia de conversión implícita sea una secuencia de conversión mejor o peor que otra.
Dadas estas definiciones, una función viable
F1
se define como una función mejor que otra función viableF2
si para todos los argumentos i, ICSi(F1) no es una secuencia de conversión peor que ICSi(F2), y luego- para algún argumento j , ICSj(F1) es una mejor secuencia de conversión que ICSj(F2)
O, si no es eso,
- el contexto es una inicialización por conversión definida por el usuario (ver 8.5, 13.3.1.5 y 13.3.1.6) y la secuencia de conversión estándar desde el tipo de retorno de
F1
al tipo de destino (es decir, el tipo de la entidad inicializado) es una mejor secuencia de conversión que la secuencia de conversión estándar del tipo de retorno deF2
al tipo de destino
O, si no es eso,
-
F1
es una función que no es plantilla yF2
es una especialización de plantilla de función
O, si no es eso,
-
F1
yF2
son especializaciones de plantilla de función, y la plantilla de función paraF1
es más especializada que la plantilla paraF2
de acuerdo con el parcial reglas de pedido descritas en 14.5.6.2.
-
Si hay exactamente una función viable que es una función mejor que todas las demás funciones viables, entonces es la seleccionada por resolución de sobrecarga; de lo contrario, la llamada está mal formada.
Sección 13.3.3.1.4 "Referencia vinculante" [over.ics.ref]
Cuando un parámetro de tipo de referencia enlaza directamente (8.5.3) a una expresión de argumento, la conversión implícita la secuencia es la identidad conversión, a menos que la expresión del argumento tenga un tipo que sea una clase derivada del tipo de parámetro, en cuyo caso la secuencia de conversión implícita es una conversión derivada a base (13.3.3.1). Si el parámetro se une directamente al resultado de aplicar una función de conversión a la expresión de argumento, la secuencia de conversión implícita es una secuencia de conversión definida por el usuario (13.3.3.1.2), con la segunda secuencia de conversión estándar ya sea una conversión de identidad o, si la conversión función devuelve una entidad de un tipo que es una clase derivada del tipo de parámetro, una conversión derivada a base.
Cuando un parámetro de tipo de referencia no está vinculado directamente a una expresión de argumento, la secuencia de conversión es el requerido para convertir la expresión del argumento al tipo subyacente de la referencia según a 13.3.3.1. Conceptualmente, esta secuencia de conversión corresponde a la copia-inicialización de un temporal de la tipo subyacente con el argumento expresion. Cualquier diferencia en la calificación CV de alto nivel se subsume por la inicialización en sí y no constituye una conversión.
Sección 13.3.3.2 "Clasificación de secuencias de conversión implícitas" [over.ics.rank]
13.3.3.2 define un ordenamiento parcial de secuencias de conversión implícitas basado en las relaciones better conversion secuencia y mejor conversión. Si una secuencia de conversión implícita
S1
está definida por estas reglas como mejor secuencia de conversión queS2
, entonces también es el caso de queS2
es una secuencia de conversión peor queS1
. Si la secuencia de conversiónS1
no es ni mejor ni peor que la secuencia de conversiónS2
,S1
yS2
se dice que ser secuencias de conversión indistinguibles.-
Al comparar las formas básicas de secuencias de conversión implícitas (como se define en 13.3.3.1)
-
Una secuencia de conversión estándar (13.3.3.1.1) es una mejor conversión que una secuencia de conversión definida por el usuario o una secuencia de conversión de puntos suspensivos, y
Una secuencia de conversión definida por el usuario (13.3.3.1.2) es una mejor secuencia de conversión que una secuencia de conversión de puntos suspensivos (13.3.3.1.3).
-
-
Dos secuencias de conversión implícitas de la misma forma son secuencias de conversión indistinguibles a menos que una de se aplican las siguientes reglas:
-
La secuencia de conversión estándar
S1
es una mejor secuencia de conversión que la secuencia de conversión estándarS2
si- S1 es una subsecuencia propia de S2 (comparando las secuencias de conversión en la forma canónica definida por 13.3.3.1.1, excluyendo cualquier transformación Lvalue; la secuencia de conversión de identidad se considera una subsecuencia de cualquier secuencia de conversión sin identidad)
O, si no es eso,
- el rango de
S1
es mejor que el rango deS2
, oS1
yS2
tienen el mismo rango y son distinguibles por las reglas del párrafo siguiente
O, si no es eso,
-
S1
yS2
difieren solo en su conversión de calificación y producen tipos similaresT1
yT2
(4.4), respectivamente, y la firma de calificación CV del tipoT1
es un subconjunto apropiado de la firma de calificación CV del tipoT2
.
O, si no es eso,
-
S1
yS2
son consolidaciones de referencia (8.5.3) y ninguna se refiere a parámetro objeto de una función miembro no estática declarada sin un calificador ref, yS1
enlaza una referencia rvalue a un rvalue yS2
enlazan una referencia lvalue.
-
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-18 03:25:49