Несоответствие при разборе числовых литералов в соответствии со стандартной грамматикой C++

Читая стандарт С++ 17, мне кажется, что существует несоответствие между pp-number обрабатывается препроцессором, и числовыми литералами, например, user-defined-integer-literal, так как они определены для обработки " верхний "язык.

Например, следующее правильно обрабатывается как число pp-number согласно грамматике препроцессора:

123_e+1

Но помещенный в контекст фрагмента кода, совместимого с С++ 11,

int  operator"" _e(unsigned long long)
    { return 0; }

int test()
    {
    return 123_e+1;
    }

текущие компиляторы Clang или GCC (я не проверял другие) вернет ошибку, подобную этой:

unable to find numeric literal operator 'operator""_e+1'

где operator"" _e(...) не найден и попытка определить operator"" _e+1(...) будет недопустимой.

Похоже, это происходит потому, что компилятор сначала использует лексему токен как pp-number, но затем не может выполнить откат и применить грамматические правила для определяемого user-defined-integer-literal при синтаксическом анализе конечного выражения.

Для сравнения: следующий код компилируется нормально:

int  operator"" _d(unsigned long long)
    { return 0; }

int test()
    {
    return 0x123_d+1;  // doesn't lex as a 'pp-number' because 'sign' can only follow [eEpP]
    }

Это правильное прочтение стандарта? И если да, то разумно ли, чтобы компилятор обрабатывал этот, возможно, редкий, угловой случай?

Ответ 1

Вы стали жертвой правила максимального жаворонка, при котором лексический анализатор принимает как можно больше символов, чтобы сформировать действительный токен.

Это описано в разделе [lex.pptoken] p3, где говорится (выделено мое):

В противном случае следующий токен предварительной обработки - это самая длинная последовательность символов, которая может составлять токен предварительной обработки, даже если это приведет к сбою в дальнейшем лексическом анализе, за исключением того, что имя заголовка ([lex.header]) формируется только внутри #include директивы.

и включает в себя несколько примеров:

[ Пример:

#define R "x"
const char* s = R"y";           // ill-formed raw string, not "x" "y"

- конец примера]

4 [Пример: фрагмент программы 0xe + foo анализируется как токен числа предварительной обработки (тот, который не является допустимым плавающим или целочисленным литеральным токеном), даже если синтаксический анализ трех токенов предварительной обработки 0xe, + и foo может привести к действительному выражению (например, если бы foo был макросом, определенным как 1). Аналогично, фрагмент программы 1E1 анализируется как номер предварительной обработки (тот, который является допустимым плавающим литеральным токеном), независимо от того, является ли E именем макроса. - конец примера]

5 [Пример: фрагмент программы x +++++ y анализируется как x ++ ++ + y, что, если x и y имеют целочисленные типы, нарушает ограничение на операторы приращения, даже если синтаксический анализ x ++ + ++ y может дать правильное выражение. - конец примера]

Это правило действует в нескольких других хорошо известных случаях, таких как a +++++ b и токены> =, для которых требуется исправление.

Для справки грамматика pp-токена выглядит следующим образом:

pp-number:  
  digit  
  . digit  
  pp-number digit  
  pp-number identifier-nondigit 
  pp-number ' digit  
  pp-number ' nondigit    
  pp-number e sign  
  pp-number E sign  
  pp-number p sign  
  pp-number P sign  
  pp-number .  

Обратите внимание на производство e sign, которое и привлекает этот случай. Если, с другой стороны, вы используете d как ваш второй пример, вы не попали бы в это (смотрите его вживую).

Кроме того, добавление пробелов также решило бы вашу проблему, так как вы больше не подвергались бы максимальному жаворонку (см. Это в прямом эфире на кресте):

123_e + 1