Prawidłowa składnia wywołania pseudo-destruktora dla zmiennej swobodnej

9

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 clangi gccpoproś 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?

Vlad z Moskwy
źródło
1
Umieszczenie spacji 0.fi .Tpowoduje, że zarówno GCC, jak i Clang to zaakceptują ...
Chris
1
Jak również(0.f).T::~T();
papieros
Prosty float f = 1.0f.t;spowoduje błąd dotyczący literału liczbowego.
1201ProgramAlarm
A floatjest 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.
Jesper Juhl
@JesparJuhl to nie jest destruktor, ale pseudoinstruktor, właśnie się dowiedziałem, że istnieje. Informacja o tagu ma przykład (który również ma nieuzasadnione wywołanie do destruktora btw)
idclev 463035818

Odpowiedzi:

3

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

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

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 jako 0.f.T :: ~ T ( ) ;, mimo że 0.f.Tnie jest to prawidłowy token numeryczny.

Zatem kod nie jest poprawny pod względem składniowym.

Eric M. Schmidt
źródło
Co ciekawe, istnieje tak naprawdę przykład z przyzwoitym podobieństwem w [lex.pptoken]: eel.is/c++draft/lex.pptoken#5
Chris
1

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.Tjako 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 z fFlL). fMoż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.

1201ProgramAlarm
źródło
Dlaczego jest interpretowany jako sufiks ud?
Vlad z Moskwy
@VladfromMoscow Jest 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 z fFlL). fMoż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 .
1201ProgramAlarm
@ 1201ProgramAlarm: Chociaż fmoże być interpretowany jako sufiks ud, f.Tnie powinien być, ponieważ .nie może być w identyfikatorze. ale to jest ... Powiedziałbym błąd kompilatora, ale jestem pewien, że jest bardziej skomplikowany.
Jarod42