Considere o seguinte programa demonstrativo.
#include <iostream>
int main()
{
typedef float T;
0.f.T::~T();
}
Este programa é compilado por Microsoft Visual Studio Community 2019
.
Mas clang
e gcc
emita um erro como este
prog.cc:7:5: error: unable to find numeric literal operator 'operator""f.T'
7 | 0.f.T::~T();
| ^~~~~
Se escrever a expressão assim ( 0.f ).T::~T()
, todos os três compiladores compilarão o programa.
Então surge uma pergunta: esse registro é 0.f.T::~T()
sintaticamente válido? E se não, então que regra sintática está quebrada?
c++
syntax
floating-point
c++17
pseudo-destructor
Vlad de Moscou
fonte
fonte
0.f
e.T
faz com que o GCC e o Clang aceitem isso ...(0.f).T::~T();
float f = 1.0f.t;
produzirá o erro sobre o literal numérico.float
é um tipo embutido , não possui um destruidor para você chamar. O que você está fazendo manualmente chamando destruidores? Fora do território de colocação de novo, isso deve ser um grande não-não.Respostas:
A análise de tokens numéricos é bastante grosseira e permite muitas coisas que não são realmente números válidos. No C ++ 98, a gramática para um "número de pré-processamento", encontrada em [lex.ppnumber], é
Aqui, "sem dígito" é qualquer caractere que possa ser usado em um identificador, exceto dígitos, e um "sinal" é + ou -. Os padrões posteriores expandiriam a definição para permitir aspas simples (C ++ 14) e seqüências no formato p-, p +, P-, P + (C ++ 17).
O resultado é que, em qualquer versão do padrão, enquanto um número de pré-processamento é necessário para iniciar com um dígito ou um período seguido de um dígito, após o qual uma sequência arbitrária de dígitos, letras e períodos pode ser seguida. Usando a regra munch máxima, segue-se que
0.f.T::~T();
é necessário tokenizar como0.f.T :: ~ T ( ) ;
, mesmo que0.f.T
não seja um token numérico válido.Portanto, o código não é sintaticamente válido.
fonte
Um sufixo literal definido pelo usuário, ud-suffix , é um identificador . Um identificador é uma sequência de letras (incluindo alguns caracteres não ASCII), o sublinhado e os números que não começam com um número. O caractere de ponto final não está incluído.
Portanto, é um erro do compilador, pois trata a sequência não identificadora
f.T
como um identificador.O
0.
é uma constante fracionária , que pode ser seguida por um expoente opcional e, em seguida, um sufixo ud (para um literal definido pelo usuário) ou um sufixo de ponto flutuante (um defFlL
). Tambémf
pode ser considerado um ud-suffx , mas como corresponde a outro tipo literal, deve ser esse e não o UDL. Um sufixo-ud é definido na gramática como um identificador.fonte
0.
é uma constante fracionária . Isso pode ser seguido por (excluindo o material do expoente) um sufixo ud (para um literal definido pelo usuário) ou um sufixo de ponto flutuante (um dosfFlL
). Tambémf
pode ser considerado um ud-suffx , mas como corresponde a outro tipo literal, deve ser esse e não o UDL. Um sufixo-ud é definido na gramática como um identificador .f
pode ser interpretado como ud-sufixo,f.T
não deve ser como.
identificador. mas é ... eu diria um bug do compilador, mas com certeza é mais complicado.