Syntaxe valide de l'appel d'un pseudo-destructeur pour une constante flottante

9

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 clanget 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?

Vlad de Moscou
la source
1
Mettre un espace entre 0.fet .Tamène GCC et Clang à accepter cela ...
chris
1
Ainsi que(0.f).T::~T();
cigien
Un simple float f = 1.0f.t;produira l'erreur sur le littéral numérique.
1201ProgramAlarm
A floatest 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.
Jesper Juhl
@JesparJuhl ce n'est pas un destructeur mais un pseudo destructeur, je viens de savoir qu'il existe. L'info de balise a un exemple (qui a également un appel non justifié au destructeur btw)
idclev 463035818

Réponses:

3

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

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

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 que 0.f.T :: ~ T ( ) ;, même s'il 0.f.Tne s'agit pas d'un jeton numérique valide.

Ainsi, le code n'est pas syntaxiquement valide.

Eric M Schmidt
la source
Fait intéressant, il y a en fait un exemple avec une similitude décente dans [lex.pptoken]: eel.is/c++draft/lex.pptoken#5
chris
1

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.Tcomme 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 des fFlL). Le fpeut é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.

1201ProgramAlarm
la source
Pourquoi est-il interprété comme un suffixe ud?
Vlad de Moscou
@VladfromMoscow La 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 des fFlL). Le fpeut é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 .
1201ProgramAlarm
@ 1201ProgramAlarm: Alors que fpeut être interprété comme suffixe ud, f.Tne 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é.
Jarod42