Подтвердить что ты не робот

Каковы все проблемы синтаксиса, связанные с использованием угловых скобок в шаблонах С++?

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

Ответ 5

Удачи, выясняя, что это делает:

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

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

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

Set!int foo;

Ответ 6

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

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

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

Ответ 7

В конечном итоге то, что должен сделать любой компилятор, переводит ваш полу-английский исходный код на любом языке - в настоящий машинный код, на котором фактически может работать компьютер. Это, в конечном счете, серия невероятно сложных математических ТРАНСФОРМОВ.

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

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

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

Ответ 8

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

Не удается:

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

Работает:

void f(A<0> =0);

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

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