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?

Author: Johannes Schaub - litb, 2011-03-18

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.

 12
Author: Johannes Schaub - litb,
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;
    }
}
 4
Author: Ben Voigt,
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]

  1. 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ón G, 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 viable F2 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 de F2 al tipo de destino

    O, si no es eso,

    • F1 es una función que no es plantilla y F2 es una especialización de plantilla de función

    O, si no es eso,

    • F1 y F2 son especializaciones de plantilla de función, y la plantilla de función para F1 es más especializada que la plantilla para F2 de acuerdo con el parcial reglas de pedido descritas en 14.5.6.2.
  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]

  1. 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.

  2. 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]

  1. 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 que S2, entonces también es el caso de que S2 es una secuencia de conversión peor que S1. Si la secuencia de conversión S1 no es ni mejor ni peor que la secuencia de conversión S2, S1 y S2 se dice que ser secuencias de conversión indistinguibles.

  2. 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).

  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ándar S2 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 de S2, o S1 y S2 tienen el mismo rango y son distinguibles por las reglas del párrafo siguiente

      O, si no es eso,

      • S1 y S2 difieren solo en su conversión de calificación y producen tipos similares T1 y T2 (4.4), respectivamente, y la firma de calificación CV del tipo T1 es un subconjunto apropiado de la firma de calificación CV del tipo T2.

      O, si no es eso,

      • S1 y S2 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, y S1 enlaza una referencia rvalue a un rvalue y S2 enlazan una referencia lvalue.
 2
Author: Ben Voigt,
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