sobrecarga sencillos operadores matrices ejemplos c++ arrays reference c++11 overload-resolution

sencillos - sobrecarga de operadores c++ matrices



Resolución de sobrecarga y matrices: ¿a qué función se debe llamar? (3)

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); }

¿Cuál f debería ser llamada? ¿Por qué?

Las últimas versiones publicadas de tres compiladores no están de acuerdo con 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 con Visual C ++ 2010 SP1
  • (3) se llama cuando el programa se compila usando Clang 3.0 (trunk 127530)

¿Las reglas de resolución de sobrecarga han cambiado sustancialmente en diferentes borradores de C ++ 0x? O, ¿son dos de estos compiladores realmente completamente equivocados? ¿Cuál es la sobrecarga correcta que se seleccionará según el último borrador de C ++ 0x?


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 un no-estático función miembro declarada sin un calificador de ref, y S1 une una referencia rvalue a un valor r y S2 une una referencia lvalue ".

(3) es mejor que ambos (1) y (2) porque es una conversión de identidad (las otras son conversiones de coincidencia exacta) y "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 adecuada de S2 (comparando las secuencias de conversión en la forma canónica definida por 13.3.3.1.1, excluyendo cualquier Transformación de Lvalue; la secuencia de conversión de identidad se considera una subsecuencia de cualquier secuencia de conversión de no identidad) "

Plantilla vs no plantilla solo se considera cuando ninguna conversión es mejor "o, si no es que ..."

aunque bastante extraño, Comeau prefiere (2) sobre (3). Este caso de prueba no se compila:

#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; } }


Esta es una respuesta de la wiki de la comunidad para recopilar fragmentos de la norma (borrador 3225).

sección 13.3.3 "Mejor función viable" [over.match.best]

  1. De fi ne ICSi (F) como sigue:

    • si F es una función miembro estática, ICS1 (F) se define de manera tal 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 otra manera,

    • deje que ICSi (F) denote la secuencia de conversión implícita que convierte el i-th argumento en la lista al tipo del i-th 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 mejor secuencia de conversión o una secuencia de conversión peor que otra.

    Dadas estas definiciones, una función viable F1 se define como una mejor función 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 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 del tipo de retorno de F1 al tipo de destino (es decir, el tipo de la entidad que se está inicializando) 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 eso,

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

    o, si no 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 acuerdo con las reglas de ordenamiento parcial 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 "Enlace de referencia" [over.ics.ref]

  1. Cuando un parámetro de tipo de referencia se une directamente (8.5.3) a una expresión de argumento, la secuencia de conversión implícita es la conversión de identidad, a menos que la expresión de argumento tenga un tipo que sea una clase derivada del tipo de parámetro, en cuyo caso la conversión implícita secuencia 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 función de conversió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 la requerida para convertir la expresión de argumento al tipo subyacente de la referencia de acuerdo con 13.3.3.1. Conceptualmente, esta secuencia de conversión corresponde a una copia inicializadora de un temporal del tipo subyacente con la expresión de argumento. Cualquier diferencia en la cali fi cación cv de nivel superior es subsumida por la inicialización en sí misma 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 orden parcial de las secuencias de conversión implícitas en función de las relaciones, mejor secuencia de conversión y mejor conversión. Si estas reglas de fi nen una secuencia de conversión implícita S1 para que sea una mejor secuencia de conversión que S2 , también es cierto que S2 es una secuencia de conversión peor que S1 . Si la secuencia de conversión S1 no es mejor ni peor que la secuencia de conversión S2 , se dice que S1 y S2 son secuencias de conversión indistinguibles.

  2. Al comparar las formas básicas de las secuencias de conversión implícitas (como se de fi ne en 13.3.3.1)

    • una secuencia de conversión estándar (13.3.3.1.1) es una mejor secuencia de 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 de fi nida 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 se aplique una de 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 correcta de S2 (comparando las secuencias de conversión en la forma canónica definida por 13.3.3.1.1, excluyendo cualquier Transformación de Lvalue; la secuencia de conversión de identidad se considera una subsecuencia de cualquier secuencia de conversión de no identidad)

      o, si no eso,

      • el rango de S1 es mejor que el de S2 , o S1 y S2 tienen el mismo rango y son distinguibles por las reglas en el párrafo a continuación

      o, si no 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 de tipo T1 es un subconjunto adecuado de la firma de calificación cv de tipo T2 .

      o, si no eso,

      • 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 de referencia, y S1 une a una referencia de rvalue con un valor de r y S2 une a una referencia de lvalue.

Primero, la secuencia de conversión de los tres es la misma, excepto que para los dos primeros, hay una transformación de lvalue (conversión de 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 13.3.3.2p3 las reglas en 13.3.3.2p3 , te detienes en este párrafo

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 une una referencia rvalue a un rvalue y S2 une una referencia lvalue.

No se puede formar una secuencia de conversión si requiere vincular una referencia de valor a un valor de l, la especificación dice en 13.3.3.1.4p3. Si observa cómo funciona el enlace de referencia en la última viñeta de 8.5.3p5, creará un temporal (creo que significó rvalue temporal) de tipo char const* partir de la matriz lvalue y vinculará la referencia a ese temporal. Por lo tanto, creo que (1) es mejor que (2) . Lo mismo vale para (1) contra (3) , aunque no lo necesitaríamos porque (3) es una plantilla, así que en un empate, elegiríamos (1) nuevamente.

En n3225 , cambiaron las reglas de enlace de referencia para que las referencias de valor r puedan vincularse a las expresiones de inicialización que son valores de l, siempre que la referencia se vincule a un valor de r (posiblemente creado al convertir el inicializador correctamente antes). Esto podría influir en el manejo por Visual C ++, que puede no estar actualizado aquí.

No estoy seguro de clang. Incluso si ignorara (1) , entonces terminaría en un empate entre (2) y (3) , y tendría que elegir (2) porque no es una plantilla.

Creo que la última viñeta de 8.5.3p5 es confusa porque dice "De lo contrario, es un tipo temporal ...". No está claro si lo temporal se considera como un valor de l o un valor de r. Por 13.3.3.1.4p3, lo que significa que no estoy seguro de cómo debe comportarse lo siguiente 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 se trata como un valor por la cláusula 13, entonces vincularemos un valor de referencia a un valor en la segunda función y un valor en la primera. Por lo tanto, elegiremos la segunda función y luego obtendremos un diagnóstico por 8.5.3p5 última bala porque T1 y T2 están relacionados con la referencia. Si asumimos que el temporal se trata como un lvalor por la cláusula 13, lo siguiente no funcionaría

void f(int &&); int main() { f(0); }

Debido a que uniríamos un rvalue ref a un lvalue que por la cláusula 13 hará que la función no sea viable. Y si interpretamos "unir un rvalue ref a un lvalue" para referirnos a la expresión inicializadora en lugar de a la expresión final vinculada, 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 al comité sobre esto.