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 clang
y gcc
emitir 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.f
y.T
hace que tanto GCC como Clang acepten esto ...(0.f).T::~T();
float f = 1.0f.t;
producirá el error sobre el literal numérico.float
es 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.T
no 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.T
como 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énf
se 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énf
se 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 .f
puede interpretarse como sufijo ud,f.T
no 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.