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?
                    
                        c++
                                syntax
                                floating-point
                                c++17
                                pseudo-destructor
                                
                    
                    
                        Vlad de Moscú
fuente
                
                fuente

0.fy.Thace que tanto GCC como Clang acepten esto ...(0.f).T::~T();float f = 1.0f.t;producirá el error sobre el literal numérico.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.Respuestas:
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
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 como0.f.T :: ~ T ( ) ;, aunque0.f.Tno sea un token numérico válido.Por lo tanto, el código no es sintácticamente válido.
fuente
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 defFlL). Tambiénfse 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.fuente
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 defFlL). Tambiénfse 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 .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.