Sintaxis válida de llamar al pseudodestructor para una constante flotante

9

Considere el siguiente programa demostrativo.

#include <iostream>

int main()
{
    typedef float T;

    0.f.T::~T();
}

Este programa está compilado por Microsoft Visual Studio Community 2019.

Pero clangy gccemitir un error como este

prog.cc:7:5: error: unable to find numeric literal operator 'operator""f.T'
    7 |     0.f.T::~T();
      |     ^~~~~

Si escribir la expresión como ( 0.f ).T::~T()entonces, los tres compiladores compilan el programa.

Entonces surge una pregunta: ¿es este registro 0.f.T::~T()sintácticamente válido? Y si no, ¿qué regla sintáctica se rompe?

Vlad de Moscú
fuente
1
Poner un espacio entre 0.fy .Thace que tanto GCC como Clang acepten esto ...
Chris
1
Así como(0.f).T::~T();
cigien
Un simple float f = 1.0f.t;producirá el error sobre el literal numérico.
1201ProgramAlarm
A floates un tipo incorporado , no tiene un destructor al que pueda llamar. ¿Qué haces incluso llamando a destructores manualmente? Fuera del territorio de ubicación nueva, eso debería ser un gran no-no.
Jesper Juhl
@JesparJuhl no es un destructor sino un pseudodestructor, acabo de saber que existe. La información de la etiqueta tiene un ejemplo (que también tiene una llamada no justificada al destructor por cierto)
idclev 463035818

Respuestas:

3

El análisis de tokens numéricos es bastante burdo y permite muchas cosas que no son números válidos. En C ++ 98, la gramática para un "número de preprocesamiento", que se encuentra en [lex.ppnumber], es

pp-number:
    digit
    . digit
    pp-number digit
    pp-number nondigit
    pp-number e sign
    pp-number E sign
    pp-number .

Aquí, un "no digito" es cualquier carácter que se puede usar en un identificador, que no sea dígitos, y un "signo" es + o -. Los estándares posteriores ampliarían la definición para permitir comillas simples (C ++ 14) y secuencias de la forma p-, p +, P-, P + (C ++ 17).

El resultado es que, en cualquier versión del estándar, se requiere un número de preprocesamiento para comenzar con un dígito, o un punto seguido de un dígito, después de eso puede seguir una secuencia arbitraria de dígitos, letras y puntos. Usando la regla de munch máxima, se deduce que 0.f.T::~T();se requiere ser tokenizado como 0.f.T :: ~ T ( ) ;, aunque 0.f.Tno sea un token numérico válido.

Por lo tanto, el código no es sintácticamente válido.

Eric M Schmidt
fuente
Curiosamente, en realidad hay un ejemplo con una similitud decente en [lex.pptoken]: eel.is/c++draft/lex.pptoken#5
chris
1

Un sufijo literal definido por el usuario, ud-suffix , es un identificador . Un identificador es una secuencia de letras (incluidos algunos caracteres no ASCII), el guión bajo y los números que no comienzan con un número. El carácter de período no está incluido.

Por lo tanto, es un error del compilador, ya que trata la secuencia sin identificador f.Tcomo un identificador.

El 0.es una constante fraccional , que puede ser seguida por un exponente opcional, luego un sufijo ud (para un literal definido por el usuario) o un sufijo de punto flotante (uno de fFlL). También fse puede considerar un sufijo ud , pero como coincide con otro tipo literal, debería ser ese y no el UDL. Un sufijo ud se define en la gramática como un identificador.

1201 Programa Alarma
fuente
¿Por qué se interpreta como un sufijo ud?
Vlad desde Moscú
@VladfromMoscow The 0.es una constante fraccional . Esto puede ser seguido por (excluyendo el material exponente) un sufijo ud (para un literal definido por el usuario) o un sufijo de punto flotante (uno de fFlL). También fse puede considerar un sufijo ud , pero como coincide con otro tipo literal, debería ser ese y no el UDL. Un sufijo ud se define en la gramática como un identificador .
1201ProgramAlarm
@ 1201ProgramAlarm: Mientras que fpuede interpretarse como sufijo ud, f.Tno debe ser como .no puede estar en el identificador. pero es ... Yo diría un error del compilador pero estoy bastante seguro de que es más complicado.
Jarod42