int numeral -> reglas de conversión de puntero

19

Considere el siguiente código.

void f(double p) {}
void f(double* p) {}

int main()
{ f(1-1); return 0; }

MSVC 2017 no compila eso. Se da cuenta de que hay una llamada sobrecargada ambigua, ya que 1-1es lo mismo 0y, por lo tanto, se puede convertir double*. Otros trucos, como 0x0, 0Lo static_cast<int>(0), tampoco funcionan. Incluso declarar ay const int Zero = 0llamar f(Zero)produce el mismo error. Solo funciona correctamente si Zerono es así const.

Parece que el mismo problema se aplica a GCC 5 e inferior, pero no a GCC 6. Tengo curiosidad por saber si esto es parte del estándar C ++, un error conocido de MSVC o una configuración en el compilador. Un Google superficial no dio resultados.

usuario1334767
fuente

Respuestas:

18

MSVC considera 1-1que es una constante de puntero nulo. Esto era correcto según el estándar para C ++ 03, donde todas las expresiones constantes integrales con valor 0eran constantes de puntero nulo, pero se cambió para que solo los literales enteros cero sean constantes de puntero nulo para C ++ 11 con el problema 903 de CWG . Este es un cambio radical, como puede ver en su ejemplo y como también está documentado en el estándar, consulte [diff.cpp03.conv] del estándar C ++ 14 (borrador N4140).

MSVC aplica este cambio solo en el modo de conformidad. Por lo tanto, su código se compilará con la /permissive-bandera, pero creo que el cambio se implementó solo en MSVC 2019, consulte aquí .

En el caso de GCC, GCC 5 pasa de manera predeterminada al modo C ++ 98, mientras que GCC 6 y luego pasa al modo C ++ 14, por lo que el cambio de comportamiento parece depender de la versión de GCC.

Si llama fcon una constante de puntero nulo como argumento, entonces la llamada es ambigua, porque la constante de puntero nulo puede convertirse en un valor de puntero nulo de cualquier tipo de puntero y esta conversión tiene el mismo rango que la conversión de int(o cualquier tipo integral) a double.

nuez
fuente
-1

El compilador funciona correctamente, de acuerdo con [over.match] y [conv] , más específicamente [conv.fpint] y [conv.ptr].

Una secuencia de conversión estándar es [bla, bla] Cero o una [...] conversión integral flotante, conversión de puntero, [...].

y

Un prvalue de un tipo entero o de un tipo de enumeración sin ámbito se puede convertir en un prvalue de un tipo de punto flotante. El resultado es exacto si es posible [bla bla]

y

Una constante de puntero nulo es un literal entero con valor cero o [...]. Una constante de puntero nulo se puede convertir a un tipo de puntero; el resultado es el valor de puntero nulo de ese tipo [bla, bla, bla]

Ahora, la resolución de sobrecarga es elegir la mejor combinación entre todas las funciones candidatas (que, como una característica divertida, ¡ni siquiera tiene que estar accesible en la ubicación de la llamada!). La mejor coincidencia es la que tiene parámetros exactos o, alternativamente, la menor cantidad posible de conversiones. Pueden ocurrir cero o una conversión estándar (... para cada parámetro), y cero es "mejor" que uno.

(1-1)es un entero literal con valor 0.

Puede convertir el literal entero cero a cada uno de los dos doubleo double*(o nullptr_t), con exactamente una conversión. Entonces, suponiendo que se declare más de una de estas funciones (como es el caso en el ejemplo), existe más de un solo candidato, y todos los candidatos son igualmente buenos, no existe la mejor coincidencia. Es ambiguo, y el compilador tiene razón al quejarse.

Damon
fuente
1
¿Cómo es 1-1un entero literal ? Es una expresión que contiene dos literales enteros con valor 1y un -operador.
nogal
@walnut: Probablemente se refiera a la redacción incómoda "secuencia de dígitos binarios, dígitos octales, dígitos o dígitos hexadecimales" . Esa es una redacción muy desafortunada para algo bastante "obvio", que sugiere algo que no es el caso (es decir, excluir el carácter negativo). Con solo "dígitos", y pedagógicamente según la definición de "dígito" (uno de 0 ... 9), no es posible tener ningún literal negativo (como -1). Lo cual, dado que el tipo predeterminado está firmado , sin embargo, es obviamente necesario, y es demostrablemente posible (y universalmente aceptado) también.
Damon
1
Me refiero a la gramática para el literal entero que se muestra en el enlace estándar, que no coincide 1-1. C ++ no tiene literales enteros negativos. -1es una expresión compuesta por un 1literal entero (de tipo con signo) y un -operador menos unario. Consulte también la sección "Notas" en cppreference.com .
nogal
Ciertamente es cierto que la gramática no lo tiene, pero eso es muy intrascendente. Por necesidad y por definición, C ++ tiene literales negativos, ya que a menos que añadas explícitamente u, tu literal está, por definición, firmado. Los tipos firmados tienen valores negativos (aproximadamente el 50% de los valores posibles son negativos). Es desafortunado que la gramática (por una razón que no sabría) sea engañosa de esta manera, y aunque técnicamente (según la gramática) -1 es un literal positivo, negado, por todos los demás medios es, por supuesto, negativo. literal. Al igual que 3 + 4 es un literal.
Damon
Por cierto, lo intenté 0U. Mismo problema. Lo que no probé es un enumvalor. Tal vez un nombre hubiera cambiado las cosas. Terminé escribiendo una larga expresión con decltypey remove_reference.
user1334767