Это то, что, как я считаю, произошло:
Существует два вида выражений, которые выглядят одинаково, но имеют совершенно разные значения:
(type) + expr
(expr) + expr
Первое - это выражение в стиле C, который преобразует унарное выражение + expr
в type
;второе - это двоичное выражение, которое выполняет сложение.
Чтобы устранить неоднозначность выражения в форме (something) + expr
, GCC сначала предполагает, что something
является типом, и выполняет предварительный анализ.Если это удается, тогда все выражение обрабатывается как приведенное выражение;в противном случае something
обрабатывается как выражение.
Теперь я думаю, что здесь есть ошибка: во время предварительного анализа GCC ошибочно полагает, что дедукция аргумента шаблона класса (CTAD) не может появиться, поэтому выдает ошибкукогда появляется CTAD.Но на самом деле, даже если предварительный синтаксический анализ определенно потерпит неудачу в этом случае, something
все еще может быть действительным выражением в стиле функции, и, следовательно, повторный анализ может быть успешным.
Для cat((lit('b')), lit('d'))
, lit('b') + lit('d')
и (lit('b'))
, GCC достаточно умен, чтобы понять, что они не могут быть выражением в стиле C, поэтому он не выполняет предварительный анализ.Для (lit<char>('b')) + lit('d')
в lit<char>('b')
нет CTAD, поэтому все в порядке.
Доказательство вышеприведенного анализа:
Если +
изменяется на /
(илиВ большинстве операторов, кроме -
, *
или &
), ошибки не возникает, поскольку (something) / expr
не может быть допустимым выражением приведения.
Подобная неоднозначность существует в sizeof(something)
(может бытьsizeof(type)
или sizeof(expr)
), и, как и ожидалось, sizeof(lit(0))
вызывает аналогичную ошибку.