Rozważ następujący program demonstracyjny.
#include <iostream>
int main()
{
typedef float T;
0.f.T::~T();
}
Ten program jest kompilowany przez Microsoft Visual Studio Community 2019
.
Ale clang
i gcc
poproś o taki błąd
prog.cc:7:5: error: unable to find numeric literal operator 'operator""f.T'
7 | 0.f.T::~T();
| ^~~~~
Jeśli napiszesz takie wyrażenie, ( 0.f ).T::~T()
to wszystkie trzy kompilatory skompilują program.
Powstaje więc pytanie: czy ten rekord jest 0.f.T::~T()
poprawny pod względem składniowym? A jeśli nie, to jaka zasada składniowa jest złamana?
c++
syntax
floating-point
c++17
pseudo-destructor
Vlad z Moskwy
źródło
źródło
0.f
i.T
powoduje, że zarówno GCC, jak i Clang to zaakceptują ...(0.f).T::~T();
float f = 1.0f.t;
spowoduje błąd dotyczący literału liczbowego.float
jest wbudowanym typem, nie ma destruktywatora do wywołania. Co robisz nawet ręcznie wywołując destruktory? Poza nowym miejscem docelowym powinno być duże nie-nie.Odpowiedzi:
Analiza tokenów numerycznych jest dość prymitywna i pozwala na wiele rzeczy, które w rzeczywistości nie są poprawnymi liczbami. W C ++ 98 gramatyka „liczby przetwarzania wstępnego”, znaleziona w [lex.ppnumber], to
W tym przypadku „niecyfrowe” to dowolny znak, który może być użyty w identyfikatorze, inny niż cyfry, a „znak” to albo + albo -. Późniejsze standardy rozszerzyłyby definicję, aby umożliwić pojedyncze cytaty (C ++ 14) i sekwencje w postaci p-, p +, P-, P + (C ++ 17).
Rezultatem jest to, że w dowolnej wersji standardu, podczas gdy numer przetwarzania wstępnego jest wymagany od cyfry lub kropki po cyfrze, po tym może nastąpić dowolna sekwencja cyfr, liter i kropek. Stosując regułę maksymalnego chrupania,
0.f.T::~T();
konieczne jest tokenizowanie jako0.f.T :: ~ T ( ) ;
, mimo że0.f.T
nie jest to prawidłowy token numeryczny.Zatem kod nie jest poprawny pod względem składniowym.
źródło
Zdefiniowany przez użytkownika przyrostek literalny, ud-przyrostek , jest identyfikatorem . Identyfikator jest ciągiem liter (w tym niektóre znaki nie-ASCII), podkreślenia i liczb, które nie zaczynają się od cyfry. Znak kropki nie jest uwzględniony.
Dlatego jest to błąd kompilatora, ponieważ traktuje sekwencję bez identyfikatora
f.T
jako identyfikator.Jest
0.
to stała ułamkowa , po której może następować opcjonalny wykładnik, następnie sufiks ud (dla literału zdefiniowanego przez użytkownika) lub sufiks zmiennoprzecinkowy (jeden zfFlL
).f
Można uznać za ud-suffx jak dobrze, ale ponieważ pasuje inny rodzaj dosłowne powinno być tak, że a nie UDL. UD sufiks zdefiniowano w gramatyki identyfikator.źródło
0.
to stała ułamkowa . Następnie może (po wyłączeniu wykładnika) sufiks ud (dla literału zdefiniowanego przez użytkownika) lub sufiks zmiennoprzecinkowy (jeden zfFlL
).f
Można uznać za ud-suffx jak dobrze, ale ponieważ pasuje inny rodzaj dosłowne powinno być tak, że a nie UDL. UD sufiks definiuje gramatyki jako identyfikator .f
może być interpretowany jako sufiks ud,f.T
nie powinien być, ponieważ.
nie może być w identyfikatorze. ale to jest ... Powiedziałbym błąd kompilatora, ale jestem pewien, że jest bardziej skomplikowany.