Considérez le programme de démonstration suivant.
#include <iostream>
int main()
{
typedef float T;
0.f.T::~T();
}
Ce programme est compilé par Microsoft Visual Studio Community 2019
.
Mais clang
et gcc
émettre une erreur comme celle-ci
prog.cc:7:5: error: unable to find numeric literal operator 'operator""f.T'
7 | 0.f.T::~T();
| ^~~~~
Si pour écrire l'expression comme ( 0.f ).T::~T()
alors, les trois compilateurs compilent le programme.
Une question se pose donc: cet enregistrement est-il 0.f.T::~T()
syntaxiquement valide? Et sinon, quelle règle syntaxique est violée?
c++
syntax
floating-point
c++17
pseudo-destructor
Vlad de Moscou
la source
la source
0.f
et.T
amène GCC et Clang à accepter cela ...(0.f).T::~T();
float f = 1.0f.t;
produira l'erreur sur le littéral numérique.float
est un type intégré , il n'a pas de destructeur à appeler. Que faites-vous même d'appeler manuellement des destructeurs? En dehors du placement-nouveau territoire, cela devrait être un grand non-non.Réponses:
L'analyse des jetons numériques est assez grossière et permet de nombreuses choses qui ne sont pas réellement des nombres valides. En C ++ 98, la grammaire d'un "numéro de prétraitement", trouvé dans [lex.ppnumber], est
Ici, un "non-chiffre" est n'importe quel caractère qui peut être utilisé dans un identifiant, autre que des chiffres, et un "signe" est soit + soit -. Les normes ultérieures élargiraient la définition pour autoriser les guillemets simples (C ++ 14) et les séquences de la forme p-, p +, P-, P + (C ++ 17).
Le résultat est que, dans n'importe quelle version de la norme, alors qu'un numéro de prétraitement est requis pour commencer par un chiffre ou une période suivie d'un chiffre, après quoi une séquence arbitraire de chiffres, de lettres et de périodes peut suivre. En utilisant la règle du grignotage maximal, il s'ensuit qu'il
0.f.T::~T();
doit être symbolisé en tant que0.f.T :: ~ T ( ) ;
, même s'il0.f.T
ne s'agit pas d'un jeton numérique valide.Ainsi, le code n'est pas syntaxiquement valide.
la source
Un suffixe littéral défini par l'utilisateur, ud-suffix , est un identifiant . Un identifiant est une séquence de lettres (y compris certains caractères non ASCII), le trait de soulignement et les nombres qui ne commencent pas par un nombre. Le caractère point n'est pas inclus.
Il s'agit donc d'un bogue du compilateur car il traite la séquence non identifiante
f.T
comme un identifiant.Il
0.
s'agit d'une constante fractionnaire , qui peut être suivie d'un exposant facultatif, puis d'un suffixe ud (pour un littéral défini par l'utilisateur) ou d'un suffixe à virgule flottante (l'un desfFlL
). Lef
peut également être considéré comme un suffixe ud , mais comme il correspond à un autre type littéral, il doit être celui-là et non l'UDL. Un suffixe ud est défini dans la grammaire comme identifiant.la source
0.
est une constante fractionnaire . Cela peut être suivi (à l'exclusion des éléments exposants) d'un suffixe ud (pour un littéral défini par l'utilisateur) ou d'un suffixe à virgule flottante (l'un desfFlL
). Lef
peut également être considéré comme un suffixe ud , mais comme il correspond à un autre type littéral, il doit être celui-là et non l'UDL. Un suffixe ud est défini dans la grammaire comme identifiant .f
peut être interprété comme suffixe ud,f.T
ne devrait pas comme.
ne peut pas être dans l'identifiant. mais c'est ... je dirais bogue du compilateur mais je suis sûr que c'est plus compliqué.