Какие синтаксические проблемы возникают при использовании угловых скобок в шаблонах C ++? - PullRequest
28 голосов
/ 05 сентября 2011

В шаблонах C ++ создаются угловые скобки vector<int>, а языки Java и C # используют один и тот же синтаксис для своих обобщений.

Создатели D, однако, довольно открыто высказались о проблемах, которые ставят угловые скобки, и создали новый синтаксис foo!(int) & mdash; но я никогда не видел слишком много подробностей о том, какие именно проблемы создают угловые скобки.

Один из них был при создании шаблона с другим шаблоном vector<vector<int>>, что могло бы привести к тому, что некоторые (старые?) Компиляторы перепутали конечный '>> `с операторами сдвига битов или потоковой передачи. Решение состояло в том, чтобы вставить пробел между двумя угловыми скобками, но разве компиляторы не смогли разобрать этот синтаксис, в настоящее время?

Другая проблема была при использовании оператора «больше» foo<3 > 2>. Парсер подумает, что оператор на самом деле закрывает создание шаблона; исправлением было ввести круглые скобки foo<(3 > 2)>. Но я не думаю, что во многих случаях вам нужно это делать, и, во всяком случае, я бы предпочел вводить дополнительные скобки, когда они необходимы, вместо введения нового синтаксиса и всегда необходимо набрать восклицательный знак.

Какие еще проблемы существуют с угловыми скобками, которые заставили разработчиков D создать новый синтаксис?

Ответы [ 8 ]

25 голосов
/ 05 сентября 2011

но разве компиляторы теперь не могут анализировать этот синтаксис?

Конечно. Но это далеко не тривиально. В частности, это мешает вам реализовать чистое разделение между не зависящим от контекста лексером и анализатором. Это особенно утомительно для подсветки синтаксиса и других средств поддержки, которые должны анализировать C ++, но не хотят / могут реализовать полноценный синтаксический анализатор.

Это делает С ++ настолько сложным для анализа, что многие инструменты просто не будут беспокоиться. Это чистый убыток для экосистемы. Другими словами: разработка инструмента синтаксического анализа значительно дороже.

Например, ctags не удается для некоторых определений шаблонов, что делает его непригодным для использования в нашем текущем проекте C ++. Очень раздражает.

Но я не думаю, что во многих случаях вам нужно [различать угловые скобки и меньше чем]

Не имеет значения как часто вам нужно это делать. Ваш парсер все еще должен справиться с этим.

Решение D отказаться от угловых задников не представляло труда. Любой одной причины было бы достаточно, учитывая, что это чистая выгода.

24 голосов
/ 05 сентября 2011

Лично самая отвратительная проблема, которую я видел, - это вызов функций шаблона в зависимом контексте:

template <typename T>
void foo(T t) {
  t.bar<3>();
}

Это выглядит просто, но на самом деле неверно.Стандарт C ++ требует введения ключевого слова template для устранения неоднозначности t.bar < 3 против вызова метода, дающего:

t.template bar<3>(); // iirk

litb сделал несколько очень интересных сообщений о возможной интерпретации, которую может придумать компилятор.

Что касается проблемы >>, то она исправлена ​​в C ++ 0x, но требует более умных компиляторов.

8 голосов
/ 05 сентября 2011

Проблема в том, что грамматика языка не зависит от контекста. Когда программа использует токены с помощью лексера, она использует метод, называемый maximal munch , что означает, что она всегда берет самую длинную возможную строку, которая может обозначать токен. Это означает, что >> рассматривается как правильный оператор смещения битов. Итак, если у вас есть что-то вроде vector<pair<int, int>>, то >> на конце обрабатывается как правильный оператор смещения битов, а не как часть создания шаблона. Чтобы он по-другому воспринимал >> в этом контексте, он должен быть контекстно- чувствительным вместо контекста- свободным - то есть он должен фактически заботиться о контексте существа токенов разобран. Это значительно усложняет лексер и парсер. Чем сложнее лексер и синтаксический анализатор, тем выше риск ошибок - и, что более важно, тем труднее инструментам их реализовать, а значит, меньше инструментов. Когда такие вещи, как подсветка синтаксиса в IDE или редакторе кода, становятся сложными для реализации, это становится проблемой.

Используя !() - что приведет к vector!(pair!(int, int)) для того же объявления - D позволяет избежать проблемы чувствительности к контексту. D сделал ряд таких выборов в своей грамматике в явном виде с целью облегчить инструментам реализацию лексизации или синтаксического анализа, когда им нужно, чтобы делать то, что они делают. И поскольку на самом деле нет никакого недостатка в использовании !() для шаблонов, кроме того факта, что это немного чуждо программистам, которые использовали шаблоны или дженерики в других языках, использующих <>, это правильный выбор дизайна языка.

И как часто вы используете или не используете шаблоны, которые могут создавать неоднозначности при использовании синтаксиса угловых скобок - например, vector<pair<int, int>> - на самом деле не имеет отношения к языку. Инструменты должны реализовать это независимо. Решение использовать !() вместо <> полностью зависит от упрощения языка для инструментов, а не для программиста. И хотя вам может или не может нравиться синтаксис !(), он довольно прост в использовании, поэтому в конечном итоге он не доставляет программистам никаких проблем, помимо изучения его и того факта, что он может идти вразрез с их личными предпочтениями.

7 голосов
/ 05 сентября 2011

В C ++ другая проблема заключается в том, что препроцессор не понимает угловые скобки, поэтому происходит сбой:

#define FOO(X) typename something<X>::type

FOO(std::map<int, int>)

Проблема заключается в том, что препроцессор думает, что FOO вызывается с двумя аргументами: std::map<int иint>.Это пример более широкой проблемы, которая часто неоднозначна, является ли символ оператором или скобкой.

4 голосов
/ 05 сентября 2011

Получайте удовольствие, выясняя, что это делает:

bool b = A< B>::C == D<E >::F();
bool b = A<B>::C == D<E>::F();

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

Используя < и >, так как совпадающие и несоответствующие токены являются катастрофой.Что касается !(), делающего использование D более продолжительным: для обычного случая с одним аргументом () являются необязательными, например, это допустимо:

Set!int foo;
2 голосов
/ 05 сентября 2011

Я полагаю, что это были единственные случаи.

Однако, это не столько проблема пользователя , сколько проблема реализатора ,Эта, казалось бы, тривиальная разница делает намного сложнее для создания правильного парсера для C ++ (по сравнению с D).D также был разработан для удобства реализации, и поэтому старались изо всех сил, чтобы избежать появления неоднозначного кода.

(Примечание: I do найти комбинацию с восклицательным знаком перехода кбыть немного неуклюжим ... одно преимущество угловых скобок - определенно легкость печатания!)

1 голос
/ 01 февраля 2014

В конечном счете, любой компилятор должен перевести ваш полуанглийский исходный код - на любом языке - в реальный машинный код, на котором может работать компьютер.В конечном итоге это серия невероятно сложных математических преобразований.

Ну, математика говорит нам, что отображение, которое нам нужно для компиляции, является «на» или «сюръективным».Все это означает, что каждая легальная программа МОЖЕТ быть однозначно привязана к сборке.Это то, что ключевые слова языка и пунктуация, как ";"существуют, и почему каждый язык имеет их.Тем не менее, языки, такие как C ++, используют одни и те же символы, такие как "{}" и "<>" для нескольких вещей, поэтому компилятор должен добавить дополнительные шаги, чтобы произвести общее, на преобразование, которое ему нужно (это то, что вы делаете в линейномалгебра при умножении матриц).Это увеличивает время компиляции, создает значительную сложность, которая сама по себе может содержать ошибки и может ограничивать способность компилятора оптимизировать вывод.

Например, Strousoup мог использовать '@' для аргумента шаблонов - это был неиспользуемый символ, который идеально подходил бы для сообщения компиляторам, что «это и всегда будет чем-то вродешаблон».Это на самом деле преобразование 1-в-1, которое идеально подходит для аналитических инструментов.Но он не сделал;он использовал символы, которые уже сопоставлены с большим и меньшим.Одно это сразу вносит двусмысленность, и от этого становится только хуже.

Звучит так, как будто «D» решил сделать последовательность «! ()» Специальным символом, используемым только для шаблонов, как мой пример «@» выше.Я готов догадаться, что его высокотемпературный код компилируется быстрее и в результате получается меньше ошибок.

0 голосов

> = неопределенность больше или равна - еще один случай, который не был упомянут:

Неудачи:

template <int>
using A = int;
void f(A<0>=0);

Работы:

void f(A<0> =0);

Я думаю, что это не изменилось в C ++ 11, как >>.

См. Этот вопрос для более подробной информации: Почему идентификатор шаблона в "A <0> = 0"не компилируется без пробела из-за оператора" больше или равно ""> = "?

...