Це C ++ без контексту чи залежно від контексту?


405

Я часто чую твердження, що C ++ - це контекстна мова. Візьмемо такий приклад:

a b(c);

Це змінне визначення чи оголошення функції? Це залежить від значення символу c. Якщо cє змінною , то a b(c);визначає змінну, названу bтипу a. Він безпосередньо ініціалізований з c. Але якщо cце тип , то a b(c);оголошує функцію з ім'ям, bяка приймає a cі повертає a.

Якщо ви знайдете визначення без контекстних мов, це в основному скаже вам, що всі правила граматики повинні мати ліві сторони, що складаються з точно одного нетермінального символу. Контекстно-чутливі граматики, з іншого боку, допускають довільні рядки кінцевих та нетермінальних символів на лівій частині.

Переглядаючи додаток A "Мова програмування на C ++", я не зміг знайти жодного іншого граматичного правила, яке б не було нічого іншого, крім одного нетермінального символу на його лівій стороні. Це означає, що C ++ є безконтекстним. (Звичайно, кожна контекстна мова також є контекстно-чутливою у тому сенсі, що без контекстні мови утворюють підмножину контекстно-чутливих мов, але це не в цьому суть.)

Отже, чи є C ++ безтекстовим чи контекстним?


12
@CarlNorum Будь ласка, покажіть мені єдине граматичне правило C ++, яке не складається з одного нетермінального символу з лівої сторони, і я негайно вам повірю.
fredoverflow

9
IIUC це трохи залежить від того, де ви намалюєте лінію для чутливості до контексту. Я думаю, що я бачив, як люди стверджують, що майже всі статично набрані мови програмування залежать від контексту не тому, що ви не можете створити для них практичний компілятор за допомогою інструментів розбору CFG, а тому, що такі реалізації "обманюють", аналізуючи деякі недійсні програми та лише відхиляючи їх пізніше, під час перевірки типу. Отже, якщо ви вважаєте, що неправильно набрані програми не є мовою (у розумінні CS, тобто набором рядків), що повинен аналізувати парсер, більше мов, ніж C ++, залежать від контексту.

6
@DeadMG: Ні, ви помиляєтесь. У формальній теорії мови взагалі немає "розбору" чи "семантики", просто "мова", який є набором рядків.
jpalecek

27
Наразі жодної відповіді фактично не стосувалося вашого визначення поняття "без контексту граматики". На мій погляд, правильна відповідь на це запитання або цитує виробництво в додатку А, яке не відповідає вашому визначенню, або демонструє, що ваше визначення неправильне або недостатнє. Стояти на своєму!
Гонки легкості на орбіті

8
Дивіться, чи граматика D насправді без контексту? . Насправді, я думаю, всі тут повинні прочитати це питання та його відповіді!
Гонки легкості на орбіті

Відповіді:


341

Нижче наведена моя (поточна) улюблена демонстрація того, чому аналіз C ++ є (ймовірно) Тюрінг-завершеним , оскільки він показує програму, яка синтаксично правильна тоді і лише тоді, коли задане ціле число є простим.

Тому я стверджую, що C ++ не є ні контекстним, ні контекстно-чутливим .

Якщо ви дозволяєте довільні послідовності символів з обох сторін будь-якого виробництва, ви створюєте граматику типу 0 ("необмежену") в ієрархії Хомського , яка є більш потужною, ніж граматика, залежна від контексту; граматики без обмежень є Тьюрінгом. Граматика, залежна від контексту (Type-1), допускає декілька символів контексту з лівого боку виробництва, але той самий контекст повинен з'являтися і в правій частині виробництва (звідси назва "контекстно-залежна"). [1] Контекстно-чутливі граматики еквівалентні лінійно обмеженим машинам Тьюрінга .

У прикладі програми основні обчислення можуть бути виконані лінійно-обмеженою машиною Тьюрінга, тому це не зовсім доводить еквівалентність Тьюрінга, але важлива частина полягає в тому, що аналізатору необхідно виконати обчислення, щоб провести синтаксичний аналіз. Це могло бути будь-яке обчислення, виражене як опис шаблону, і є всі підстави вважати, що екземпляр шаблону C ++ є Тюрінг-повним. Дивіться, наприклад, документ Тодда Л. Вельдхуйзена за 2003 рік .

Незважаючи на те, що C ++ може бути розібраний комп'ютером, тож його, безумовно, можна було б розібрати на машині Тьюрінга. Отже, граматика без обмежень могла це визнати. Насправді писати таку граматику було б недоцільно, тому стандарт не намагається цього робити. (Дивись нижче.)

Питання про «неоднозначність» певних виразів - це переважно червона оселедець. Для початку неоднозначність - це особливість певної граматики, а не мови. Навіть якщо можна довести, що мова не має однозначних граматик, якщо вона може бути розпізнана без граматики без контексту, вона є безконтекстною. Аналогічно, якщо вона не може бути розпізнана без контекстною граматикою, але її можна розпізнати за допомогою контекстно-чутливої ​​граматики, це контекстно-залежно. Неоднозначність не має значення.

Але в будь-якому випадку, як і рядок 21 (тобто auto b = foo<IsPrime<234799>>::typen<1>();) у нижченаведеній програмі, вирази зовсім не неоднозначні; їх просто розбирають по-різному в залежності від контексту. У найпростішому вираженні питання синтаксична категорія певних ідентифікаторів залежить від того, як вони були оголошені (наприклад, типи та функції), що означає, що формальна мова повинна була б визнати той факт, що два рядки довільної довжини в ті ж програми ідентичні (декларація та використання). Це можна змоделювати за допомогою граматики "копіювати", яка є граматикою, яка розпізнає дві послідовні точні копії одного і того ж слова. Це легко довести за допомогою насосної лемищо ця мова не є контекстною. Можлива контекстно-залежна граматика для цієї мови, і граматика типу 0 надається у відповіді на це питання: /math/163830/context-sensitive-grammar-for-the- мова копіювання .

Якби спробувати написати контекстно-залежну (або необмежену) граматику для розбору C ++, це цілком можливо наповнить Всесвіт писанкарами. Написання машини Тьюрінга для розбору C ++ було б не менш можливим завданням. Навіть писати програму C ++ складно, і, наскільки я знаю, жодна з них не виявилася правильною. Ось чому стандарт не намагається надати повну формальну граматику, і чому він вирішує записати деякі правила розбору на технічній англійській мові.

Те, що схоже на формальну граматику в стандарті C ++, не є повним формальним визначенням синтаксису мови C ++. Навіть не повне формальне визначення мови після попередньої обробки, що може бути простішим формалізувати. (Це не була б мова, однак: мова С ++, визначена стандартом, включає препроцесор, і функціонування препроцесора описано алгоритмічно, оскільки це було б надзвичайно важко описати в будь-якому граматичному формалізмі. Саме в цьому розділі стандарту, де описано лексичне розкладання, включаючи правила, коли його потрібно застосовувати не один раз.)

Різні граматики (дві грамати, що перекриваються для лексичного аналізу, одна, яка має місце перед попередньою обробкою, а інша, якщо необхідно, після цього, плюс "синтаксична" граматика), зібрані в Додатку А, з цим важливим записом (наголос додано):

Цей підсумок синтаксису C ++ призначений для сприяння розумінню. Це не точне твердження мови . Зокрема, описана тут граматика приймає набір дійсних C ++ конструкцій . Правила розбірливості (6.8, 7.1, 10.2) повинні застосовуватися, щоб відрізняти вирази від декларацій. Крім того, правила контролю доступу, неоднозначності та типу повинні використовуватися для усунення синтаксично дійсних, але безглуздих конструкцій.

Нарешті, ось обіцяна програма. Рядок 21 є синтаксично правильним тоді і лише тоді, коли N у IsPrime<N>простому. В іншому випадку typenце ціле число, а не шаблон, і так typen<1>()розбирається як (typen<1)>()синтаксично неправильне, оскільки ()не є синтаксично допустимим виразом.

template<bool V> struct answer { answer(int) {} bool operator()(){return V;}};

template<bool no, bool yes, int f, int p> struct IsPrimeHelper
  : IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> {};
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> { using type = answer<false>; };
template<int f, int p> struct IsPrimeHelper<false, true, f, p> { using type = answer<true>; };

template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X { static const int i = I; int a[i]; }; 

template<typename A> struct foo;
template<>struct foo<answer<true>>{
  template<int I> using typen = X<I>;
};
template<> struct foo<answer<false>>{
  static const int typen = 0;
};

int main() {
  auto b = foo<IsPrime<234799>>::typen<1>(); // Syntax error if not prime
  return 0;
}

[1] Якщо говорити більш технічно, кожне виробництво в контексті, залежно від контексту граматики, має бути такої форми:

αAβ → αγβ

де Aє нетермінальним і α, βможливо, порожні послідовності граматичних символів, і γє не порожньою послідовністю. (Граматичні символи можуть бути або терміналами, або нетерміналами).

Це можна читати як A → γлише в контексті [α, β]. Граматика без контексту (тип 2) αі βповинна бути порожньою.

Виявляється, ви також можете обмежувати граматики "монотонним" обмеженням, коли кожне виробництво повинно мати форму:

α → βде |α| ≥ |β| > 0  ( |α|означає "довжина α")

Можна довести, що набір мов, розпізнаваних монотонними граматиками, точно такий же, як і набір мов, розпізнаваних контекстно-чутливими граматиками, і часто трапляється так, що простіше базувати докази на монотонних граматиках. Отже, досить часто бачимо, що "контекстно-чутливий" використовується так, ніби він означає "монотонне".


27
Отже, це не тільки залежно від контексту, але може бути зроблено залежно від будь-якого контексту, який ви можете виразити в шаблонах, які є Тьюрінгом.
Щеня

7
@mehrdad, ОП говорить "контекстно-залежна мова", а не контекстно-залежна граматика. Неоднозначність - особливість граматики, а не мови. Мова дійсно контекстно-залежна, але не тому, що конкретна граматика для неї неоднозначна.
rici

2
Зверніть увагу , що мій приклад НЕ неоднозначний. Це однозначне вираження дійсної програми. Якщо змінити значення у рядку 21, воно може неправильно сформуватися. Але в жодному випадку це неоднозначно.
rici

5
У мене є одне сумнів: як ви показуєте, результат оцінки шаблону може змінити різницю між добре сформованою та неправильно сформованою програмою. Оцінка шаблону завершена. Тож чи не правильно визначити, чи потрібна рядок у мові (C ++), потрібна цілісність? Як ви кажете, контекстно-чутлива мова - це "просто" "лінійний обмежений автомат", який не є повним завершенням AFAIK. Або ваш аргумент використовує обмеження, які стандарт C ++ ставить на деякі речі, включаючи глибину оцінки шаблону?

4
@AntonGolov: Моя оригінальна версія цього прикладу зробила саме це (ви можете досягти цього, поставивши 0всередину (), для простого), але я вважаю, що це цікавіше таким чином, оскільки він демонструє, що вам потрібна інстанціяція шаблону, навіть щоб визнати, якщо рядок - це синтаксично правильна програма C ++. Якщо обидві гілки збираються, то мені доведеться більше працювати, щоб оскаржити аргумент про те, що різниця "семантична". Цікаво, хоча мені часто доводиться визначати "синтаксичний", ніхто ніколи не пропонував визначення "семантичного", крім "речі, які я не вважаю синтаксичним" :)
rici

115

По-перше, ви справедливо зауважили, що в кінці стандарту C ++ у граматиці немає контекстно-чутливих правил, щоб граматика була без контексту.

Однак ця граматика не точно описує мову C ++, оскільки вона створює програми, що не належать до C ++, такі як

int m() { m++; }

або

typedef static int int;

Мова C ++, визначена як "набір добре сформованих програм C ++", не є контекстною (можна показати, що лише вимогливі змінні, що оголошуються, це роблять). З огляду на те, що ви можете теоретично писати програми, завершені Тьюрінгом, у шаблони та робити програму неправильно сформованою на основі їх результату, це навіть не залежно від контексту.

Зараз (неосвічені) люди (як правило, не теоретики мови, а дизайнери парсерів), як правило, використовують "не контекстно" у деяких наступних значеннях

  • неоднозначне
  • не можна розібратися з Бізоном
  • не LL (k), LR (k), LALR (k) або будь-який визначений парсером мовний клас, який вони обрали

Граматика в задній частині стандарту не задовольняє цим категоріям (тобто це неоднозначно, не LL (k) ...), тому граматика C ++ для них "не контекстна". І в певному сенсі вони мають рацію, проклято, досить важко створити робочий аналізатор C ++.

Зауважте, що використовувані тут властивості лише слабко пов'язані з без контекстними мовами - неоднозначність не має нічого спільного з контекстною чутливістю (насправді правила, що залежать від контексту, зазвичай допомагають розмежувати виробництво), інші два - лише підмножина контексту -безкоштовні мови. І аналіз контекстних мов не є лінійним процесом (хоча розбір детермінованих є).


7
ambiguity doesn't have anything to do with context-sensitivityЦе теж було моєю інтуїцією, тому я радий бачити, що хтось (а) погодився і (б) пояснити це там, де не зміг. Я вважаю, що вона дискваліфікує всі аргументи, на яких ґрунтується a b(c);, і частково задовольняє оригінальне запитання, чию передумову "часто чули" твердження про чутливість до контексту через неоднозначність ... особливо, якщо для граматики насправді немає двозначності навіть у MVP.
Гонки легкості на орбіті

6
@KonradRudolph: Зазначається у стандарті: "Існує визначена реалізацією кількість, яка визначає обмеження на загальну глибину рекурсивних екземплярів, яка може залучати більше одного шаблону. Результат нескінченної рекурсії в інстанції не визначений". (14.7.1p15) Я інтерпретую це, щоб означати, що впровадженню не потрібно розуміти кожну дійсну програму c ++, а не те, що програми з занадто великою глибиною рекурсії є недійсними. Єдиними, які позначені як недійсні, є ті, що мають нескінченну глибину рекурсії.
rici

3
@KonradRudolph: Я заперечую, що це "загальна довідка". Те, що я читав цю досить складну статтю і не розумію її достатньо, щоб викласти цей маленький факт, має бути достатньо для того, щоб продемонструвати це. Це не так, як якщо б ви сказали щось на кшталт "комп'ютери, які зазвичай використовують електрику", або "біти можуть бути правдивими чи помилковими".
Гонки легкості на орбіті

3
Якщо цей факт справді настільки відомий, я думаю, було б набагато простіше знайти посилання на нього, ніж довго доводити питання про те, чи потрібно його надавати. Не кажучи вже про конструктивне.
Samuel Edwin Ward

5
Наскільки я можу сказати, @Konrad помилився, коли сказав: "Контекстний сенсор еквівалентний Тьюрінгу завершеному". (принаймні, він був, якщо він позначав "Рекурсивно перелічувані" "Тьюрінг завершений"), і з тих пір не зміг визнати цю помилку. Ось посилання на належні встановлені відносини включення тут: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix

61

Так. Наступне вираз має різний порядок операцій залежно від контексту, який вирішено :

Редагувати: Коли фактичний порядок роботи змінюється, використовувати його "звичайний" компілятор, який аналізує незадекларований AST перед тим, як прикрасити його (поширює інформацію про тип), надзвичайно важко. Інші згадані в контексті речі "порівняно легкі" порівняно з цим (не те, що оцінка шаблону зовсім не проста).

#if FIRST_MEANING
   template<bool B>
   class foo
   { };
#else
   static const int foo = 0;
   static const int bar = 15;
#endif

Далі:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );

Чому цю проблему не можна вирішити, як для C, пам’ятаючи, які визначення типу є в області застосування?
Blaisorblade

1
@Blaisorblade: Один із способів зробити компілятор "чистим" - це розділити завдання на самостійні етапи ланцюга, такі як створення дерева розбору з входу, а потім кроком, який робить аналіз типу. C ++ змушує вас або 1) об'єднати ці кроки в один, або 2) проаналізувати документ відповідно до обох / усіх можливих інтерпретацій, дозволяючи етапам роздільної здатності типу звузити його до правильної інтерпретації.
Сем Харвелл

@ 280Z28: погоджено, але це теж стосується і C; Я думаю, що хороший відповідь на це питання має показати , чому C ++ гірше , ніж С. кандидатська дисертація пов'язана тут робить , що: stackoverflow.com/a/243447/53974
Blaisorblade

26

Щоб відповісти на ваше запитання, вам потрібно розрізнити два різні питання.

  1. Простий синтаксис майже кожної мови програмування не є контекстним. Зазвичай він надається у вигляді розширеної форми "Бекус-Наур" або граматики без контексту.

  2. Однак, навіть якщо програма відповідає граматиці без контексту, визначеній мовою програмування, це не обов'язково є дійсною програмою. Існує багато безконтекстних poperty, яким програма повинна задовольнитись, щоб бути дійсною програмою. Наприклад, найпростішою такою властивістю є область змінних.

На закінчення, чи є C ++ без контексту, залежить від поставленого питання.


5
Цікаво відзначити, що вам часто доводиться розміщувати рівень простого синтаксису нижче, ніж ви очікували, щоб отримати CFG для своєї мови програмування. Візьмемо, наприклад, С. Ви можете подумати, що правило граматики для простого оголошення змінної в C буде VARDECL : TYPENAME IDENTIFIER, але ви не можете цього мати, оскільки ви не можете відрізнити імена типів від інших ідентифікаторів на рівні CF. Інший приклад: на рівні CF ви не можете вирішити, чи потрібно розбирати a*bдекларацію змінної ( bтипу вказівника на a) або як множення.
LaC

2
@LaC: Так, дякую, що вказали на це! До речі, я впевнений, що існує більш поширений технічний термін для простого синтаксису . Хтось має правильний термін?
День

4
@Dan: те, про що ви говорите, - це наближення мови, яку дає деяка без контексту граматика. Звичайно, таке наближення є безконтекстним за визначенням. Це сенс, в якому часто використовується "синтаксис" при обговоренні мов програмування.
reinierpost

13

Ви можете поглянути на Дизайн та еволюція C ++ , автор Bjarne Stroustrup. У ньому він описує свої проблеми, намагаючись використовувати yacc (або подібне) для розбору ранньої версії C ++, і бажаючи, щоб він використовував натомість рекурсивний спуск.


Нічого ... Дякую. Цікаво, чи насправді має сенс думати про використання чогось більш потужного, ніж CFG, для розбору будь-якої штучної мови.
Дервін Тунк

Прекрасна книга для розуміння того, до чого сходить C ++. Я рекомендую це та Lippman's Inside C ++ Object Model, щоб зрозуміти, як працює C ++. Хоча обидва датовані, вони все ще добре читаються.
Метт Ціна

"Meta-S" - це контекстно-чутливий синтаксичний розбір Quinn Tyler Jackson. Я його не використовував, але він розповідає вражаючу історію. Перегляньте його коментарі в comp.compilers, і дивіться rnaparse.com/MetaS%20defined.htm
Іра Бакстер,

@IraBaxter: ваш x-ref сьогодні є МВС - і суцільні посилання на програмне забезпечення здаються невловимими (пошук у Google не дає жодних корисних результатів, як із металевою сторінкою: site: rnaparse.com, так і з мета-Квінном Джексоном s '; є шматочки та фрагменти, але мета-s.com призводить, наприклад, до неінформативного веб-сайту).
Джонатан Леффлер

@Jonathan: деякий час, просто помітив вашу скаргу. Не знаю, чому посилання погана, я вважав, що це добре, коли я його писав. Куінн був досить активним у comp.compilers. Google, схоже, стає делікатним, це все, що я можу знайти: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, він підписав права на MetaS на якесь спорядження на Гаваях, щоб повторно продати ринок. З огляду на те, наскільки це технічно було дивно, ІМХО підписує свій смертний вирок. Це звучало як дуже розумна схема.
Іра Бакстер

12

Так, C ++ є контекстно-чутливим, дуже контекстним. Ви не можете побудувати дерево синтаксису, просто розібравшись через файл, використовуючи контекстний вільний аналізатор, оскільки в деяких випадках вам потрібно знати символ з попередніх знань, щоб вирішити (тобто побудувати таблицю символів під час розбору).

Перший приклад:

A*B;

Це вираз множення?

АБО

Це оголошення Bзмінної є вказівником типу A?

Якщо A - змінна, то це вираз, якщо A - тип, це оголошення вказівника.

Другий приклад:

A B(bar);

Це прототип функції, що приймає аргумент barтипу?

АБО

Це оголошувати змінну Bтипу Aі викликає конструктор A з barпостійною як ініціалізатор?

Вам потрібно ще раз знати, чи barє змінною чи типом із таблиці символів.

Третій приклад:

class Foo
{
public:
    void fn(){x*y;}
    int x, y;
};

Це випадок, коли побудова таблиці символів під час розбору не допомагає, тому що оголошення x і y надходять після визначення функції. Отже, вам потрібно спочатку проаналізувати визначення класу і переглянути другий спосіб визначення в другому проході, щоб сказати, що x * y - це вираз, а не декларація вказівника чи інше.


1
A B();є оголошенням функції навіть у визначенні функції. Шукайте найбільш
роздратований

"Ви не можете побудувати дерево синтаксису, просто проаналізувавши файл" FALSE. Дивіться мою відповідь.
Іра Бакстер

10

C ++ розбирається з аналізатором GLR. Це означає, що під час розбору вихідного коду аналізатор може зіткнутися з неоднозначністю, але він повинен продовжуватись і вирішувати, яке граматичне правило використовувати пізніше .

дивись також,

Чому C ++ не можна розібрати з LR (1) парсером?


Пам’ятайте, що без контексту граматика не може описати ВСІ правила синтаксису мови програмування. Наприклад, граматика атрибутів використовується для перевірки дійсності типу виразу.

int x;
x = 9 + 1.0;

Не можна описати таке правило без граматики без контексту: Права сторона завдання повинна бути того ж типу Ліва сторона.


4
Більшість парсерів C ++ не використовують технологію розбору GLR. GCC не робить. Деякі так і роблять. Дивіться semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html для того, щоб це зробити.
Іра Бакстер

10

У мене є відчуття, що існує певна плутанина між формальним визначенням "контекстно-залежного" та неформальним використанням "контекстно-залежного". Перший має чітко визначене значення. Останнє використовується для того, щоб сказати "вам потрібен контекст для розбору вхідних даних".

Це також задається тут: Контекстна чутливість проти неоднозначності .

Ось граматика без контексту:

<a> ::= <b> | <c>
<b> ::= "x"
<c> ::= "x"

Це неоднозначно, тому для розбору введення "х" вам потрібен деякий контекст (або жити з неоднозначністю, або випромінювати "Попередження: E8271 - Введення неоднозначне в рядку 115"). Але це, звичайно, не контекстно-залежна граматика.


Як наявність декількох символів у лівій частині виробництва вирішує цю проблему? Я не думаю, що ця відповідь відповідає на питання.
користувач541686

1
Моя відповідь відповідає у відповідь на перше речення: "Я часто чую твердження, що C ++ - це контекстна мова". Якщо в цих твердженнях неформально використовується вираз "контекстно-залежний", то проблем немає. Я не думаю, що C ++ є формально залежним від контексту.
Омрі Барель

Я думаю, що C ++ є формально залежним від контексту, але проблема, яку я маю, полягає в тому, що я не розумію, наскільки контекстно-залежна граматика матиме більший успіх у аналізі C ++, ніж у CFG.
користувач541686

6

Жодна мова, що нагадує алголь, не є контекстною, оскільки в них є правила, які обмежують вирази та заяви, які можуть з'являти ідентифікатори залежно від їх типу, і тому що немає обмежень у кількості висловлювань, які можуть виникати між декларацією та використанням.

Звичайне рішення - написати безпроблемний аналізатор, який фактично приймає набір дійсних програм і помістити контекстно-залежні частини у спеціальний "семантичний" код, приєднаний до правил.

C ++ виходить за рамки цього, завдяки своїй повній шаблонній системі Тьюрінга. Див. Питання щодо переповнення стека 794015 .




5

Він є контекстно-залежним, оскільки a b(c);має два дійсних синтаксичних синтаксиси - декларування та змінну. Коли ви говорите "Якщо cце тип", це контекст, саме там, і ви точно описали, наскільки C ++ чутливий до нього. Якщо у вас не було цього контексту "Що таке c?" ви не могли однозначно проаналізувати це.

Тут контекст виражається у виборі лексем - аналізатор читає ідентифікатор як маркер імені типу, якщо він називає тип. Це найпростіша резолюція і дозволяє уникнути значної складності бути контекстно-залежною (в даному випадку).

Редагувати: Звичайно, є більше питань чутливості до контексту, я лише зосередився на тому, що ви показали. Шаблони для цього особливо неприємні.


1
Також a<b<c>>d, правда? (Ваш приклад насправді є класикою від C , де це єдина перешкода бути без контексту.)
Керрек С.Б.

Думаю, це більше питання лексінгу. Але, безумовно, в тій же категорії, так.
Щеня

2
Запитуючий не запитує, наскільки він більш контекстний, ніж C, лише щоб показати, що він контекстно-залежний.
Щеня

Тож .. чи C ++ більш чутливий до контексту, ніж C?
Керрек СБ

2
@DeadMG Я не думаю, що ти відповідаєш на питання (я теж не думаю, що я був). Як наявність клем на лівій стороні виробництва вирішує цю проблему?
користувач541686

5

Постановки в стандарті C ++ написані без контексту, але, як ми всі знаємо, мова насправді не визначає точно. Деякі з того, що більшість людей вважають двозначністю у сучасній мові, можна (я вважаю) вирішити однозначно граматикою, залежною від контексту.

Для найбільш наочний приклад, давайте розглянемо роздосадувати Розбір: int f(X);. Якщо Xє значення, то це визначається fяк змінна, яка буде ініціалізована X. Якщо Xце тип, він визначає fяк функцію, що приймає один параметр типу X.

Дивлячись на це з граматичної точки зору, ми можемо розглядати це так:

A variable_decl ::= <type> <identifier> '(' initializer ')' ';'

B function_decl ::= <type> <identifier> '(' param_decl ')' ';'

A ::= [declaration of X as value]
B ::= [declaration of X as type]

Зрозуміло, щоб бути абсолютно правильним, нам потрібно було б додати деякі додаткові "речі", щоб врахувати можливість втручання декларацій інших типів (тобто, А і В дійсно повинні бути "деклараціями, включаючи декларацію X як ..." або щось із цього наказу).

Це все ще досить відрізняється від типового CSG, хоча (або принаймні те, про що я пам'ятаю про них). Це залежить від побудованої таблиці символів - тієї частини, яка спеціально розпізнає Xяк тип або значення, не просто якийсь тип оператора, який передує цьому, а правильний тип висловлювання для правильного символу / ідентифікатора.

Як таке, мені доведеться зробити певне, щоб бути впевненим, але я негайно здогадуюсь, що це насправді не кваліфікується як CSG, принаймні, як цей термін зазвичай використовується.


(Безконтекстні) постановки досить добре визначають найбільш неприємний синтаксичний аналіз, щоб його можна було розібрати за допомогою двигуна без контекстного розбору. Це затягує проблему вирішення того, яке з декількох інтерпретацій є дійсним до моменту завершення розбору, але це просто полегшить розробку аналізатора і вирішувача імен, оскільки вони модульні, а не заплутані, як у звичайних C ++ парсерах. Дивіться AST для найбільш розгульного розбору: stackoverflow.com/questions/17388771/…
Ira Baxter

5

Найпростіший випадок безконтекстної граматики включає розбір виразів, що включають шаблони.

a<b<c>()

Це може розібратися як будь-яке

template
   |
   a < expr > ()
        |
        <
      /   \
     b     c

Або

 expr
   |
   <
 /   \
a   template
     |
     b < expr > ()
          |
          c

Два AST можуть бути відключені, лише вивчивши декларацію "a" - колишній AST, якщо "a" є шаблоном, або другий, якщо ні.


Я вважаю, що C ++ 11 вимагає останньої інтерпретації, і ви повинні додати паролі для відмови від першого.
Джозеф Гарвін

1
@JosephGarvin, ні. C ++ мандати, які <повинні бути дужкою, якщо це може бути (наприклад, це слід за ідентифікатором, який називає шаблон). C ++ 11 додав вимогу, щоб >і перший символ >>був інтерпретований як близькі дужки, якщо це використання правдоподібно. Це впливає на аналіз того, a<b>c>де aє шаблон, але не впливає на нього a<b<c>.
риці

@aaron: як це простіше, ніж a();(що є expr.callабо expr.type.conv)?
rici

@rici: На жаль, я не розумів, що це асиметрично.
Джозеф Гарвін

5
Чи описуєте ви неоднозначність чи чутливість до контексту?
corazza

4

Показано, що шаблони C ++ є Тюрінг потужними. Хоча це не формальна довідка, ось де можна подивитися в цьому плані:

http://cpptruths.blogspot.com/2005/11/c-templates-are-turing-complete.html

Я зважусь на здогадку (старий як фольклорний і стислий доказ CACM, що показує, що ALGOL у 60-х не може бути представлений CFG) і скажу, що C ++ не може бути правильно розібраний лише CFG. CFG в поєднанні з різними механізмами ТР або в дереві, або в ході скорочення - це вже інша історія. У загальному сенсі, через проблему зупинки, існує якась програма C ++, яка не може бути показана правильною / неправильною, але, тим не менш, є правильною / неправильною.

{PS- Як автор Meta-S (згаданий декількома людьми вище) - я з упевненістю можу сказати, що Thothic не є неіснуючим, і програмне забезпечення не доступне безкоштовно. Можливо, я сформулював цю версію своєї відповіді таким чином, що я не видаляюсь і не голосую до -3.}



2

Очевидно, якщо ви ставите питання дослівно, майже всі мови з ідентифікаторами залежать від контексту.

Потрібно знати, чи є ідентифікатором ім'я типу (ім’я класу, ім'я, введене typedef, параметр шаблону назви типу), ім'я шаблону чи якесь інше ім'я, щоб мати змогу правильно використати ідентифікатор. Наприклад:

x = (name)(expression);

- це команда if, якщо nameце ім'я типу, а виклик функції ifname це ім'я функції. Інший випадок - це так званий "найбільш збудливий синтаксичний аналіз", де неможливо диференціювати визначення змінної та декларацію функції (існує правило, яке говорить про те, що це оголошення функції).

Ця складність зумовила необхідність typenameі templateзалежних імен. Решта C ++ не є контекстно-чутливою, наскільки я знаю (тобто для неї можна написати контекстну безкоштовну граматику).


2

Meta-S "- це контекстно-чутливий синтаксичний розбір Quinn Tyler Jackson. Я його не використовував, але він розповідає вражаючу історію. Перегляньте його коментарі в comp.compilers та перегляньте rnaparse.com/MetaS%20defined.htm - Іра Бакстер, 25 липня о 10:42

Правильне посилання - це розбір загадок

Meta-S була власністю неіснуючої компанії під назвою Thothic. Я можу надіслати безкоштовну копію Meta-S всім, хто зацікавився, і я використав її в дослідженнях РНК-аналізу. Зверніть увагу, що "граматика псевдоконт", що міститься у папках прикладів, була написана небіоінформатиком, програмістом по програміровці і в основному не працює. Мої граматики застосовують інший підхід і працюють досить добре.


Це насправді цікава знахідка.
Dervin Thunk

0

Велика проблема тут полягає в тому, що терміни "без контексту" та "залежно від контексту" є мало неінтуїтивними в межах інформатики. Для C ++ контекстна чутливість нагадує багатозначність, але це не обов'язково вірно в загальному випадку.

У C / ++ оператор if дозволений лише всередині функції функції. Це могло б зробити це контекстно-залежним, правда? Ну, ні. Граматики без контексту насправді не потребують властивості, з якої можна викреслити деякий рядок коду та визначити, чи він дійсний. Це насправді не те, що означає без контексту. Це дійсно просто етикетка, яка туманно передбачає щось таке, що пов’язане з тим, як це звучить.

Тепер, якщо вислів у функціональному тілі аналізується по-різному залежно від того, що визначено за межами безпосередніх граматичних предків (наприклад, чи описує ідентифікатор тип чи змінну), як у a * b;випадку, то воно, власне, є залежним від контексту. Тут немає фактичної неоднозначності; він буде розбиратися як оголошення вказівника, якщо aце тип і множення в іншому випадку.

Бути залежним від контексту не обов'язково означає "важко розібратися". C насправді не так важко, тому що сумнозвісну a * b;"неоднозначність" можна вирішити за допомогою таблиці символів, яка містить typedefs, які зустрічалися раніше. Для вирішення цієї справи, як це робиться на C ++, вона не потребує будь-яких довільних шаблонів шаблонів (які, як було доведено, є Тьюрінг завершеними). Насправді неможливо написати програму C, яка не збиратиметься протягом певного часу, навіть якщо вона має ту саму контекстну чутливість, що і C ++.

Python (та інші чутливі до пробілу мови) також залежать від контексту, оскільки він вимагає стану в лексемі для генерування відступів і виділених лексем, але це не робить його складніше для розбору, ніж типова граматика LL-1. Він фактично використовує генератор парсера, що є частиною того, чому Python має такі неінформативні повідомлення про помилки синтаксису. Тут також важливо зазначити, що немає такої «неоднозначності», як a * b;проблема в Python, даючи хороший конкретний приклад контекстно-чутливої ​​мови без «неоднозначної» граматики (про що було сказано в першому параграфі).


-4

У цій відповіді йдеться про те, що C ++ не є контекстним ... є підказка (не відповідачем), що її неможливо проаналізувати, і відповідь пропонує складний приклад коду, який створює недійсну програму C ++, якщо певна константа не є просте число.

Як зауважували інші, питання про те, чи є мова залежною від контексту / вільною, відрізняється від того ж питання про певну граматику.

Для того, щоб поставити питання про розбірливість для спокою, я пропоную емпіричні докази того, що існують без контексту граматики для C ++, які можуть бути використані для створення AST для безтекстового розбору вихідного тексту, фактично розбираючи його з існуючим GLR -інструмент на основі парсерів, керований явною граматикою.

Так, це вдається, "прийнявши занадто багато"; не все, що вона приймає, є дійсною програмою C ++, і тому після неї додаються додаткові перевірки (тип перевірки). І так, перевірка типу може зіткнутися з проблемами з обчислюваністю. На практиці інструменти не мають цієї проблеми; якби люди писали такі програми, жодна з них не збирала б. (Я думаю, що стандарт фактично ставить обмеження на кількість обчислень, які ви можете зробити, розгортаючи шаблон, тому насправді обчислення є насправді кінцевими, але, ймовірно, досить великими).

Якщо ви маєте на увазі, визначте, чи є джерельна програма набором дійсних вихідних програм C ++ , тоді я погоджуся, що проблема набагато складніше. Але в цьому не проблема розбору .

Інструмент вирішує цю проблему, виділяючи синтаксичний аналіз від перевірки типу розібраної програми. (Якщо є кілька інтерпретацій за відсутності контексту, він записує вузол неоднозначності в дереві розбору з кількома можливими синтаксичними розбірками; перевірка типу визначає, який з них правильний, і усуває недійсні підряди). Ви можете бачити (часткове) розбір дерева на прикладі нижче; все дерево занадто велике, щоб вписатись у відповідь ТА. Зауважте, ви отримуєте дерево розбору, чи не використовується значення 234797 або 234799.

Запуск імені / типу інструмента розв'язувача інструменту над AST з початковим значенням 234799 вдається. При значенні 234797 роздільник імені не відповідає (як очікувалося) повідомленням про помилку, "typen не є типом". і, отже, ця версія не є дійсною програмою C ++.

967 tree nodes in tree.
15 ambiguity nodes in tree.
(translation_unit@Cpp~GCC5=2#6b11a20^0 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
 (declaration_seq@Cpp~GCC5=1021#6b06640^1#6b11a20:1 {10} Line 1 Column 1 File C:/temp/prime_with_templates.cpp
  (pp_declaration_seq@Cpp~GCC5=1022#6b049a0^1#6b06640:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b04980^1#6b049a0:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b04960^1#6b04980:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2082#6afbde0^1#6b04960:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter@Cpp~GCC5=2085#6afbd80^1#6afbde0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6afbd40^1#6afbd80:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6afb880^1#6afbd40:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6afb840^1#6afb880:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6afb7e0^1#6afb840:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1138#6afb7a0^1#6afb7e0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6afb7e0
   |   | )decl_specifier#6afb840
   |   |)basic_decl_specifier_seq#6afb880
   |   |(ptr_declarator@Cpp~GCC5=1417#6afbc40^1#6afbd40:2 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6afbba0^1#6afbc40:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6afbb80^1#6afbba0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6afbaa0^1#6afbb80:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6afb9c0^1#6afbaa0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afb780^1#6afb9c0:1[`V'] Line 1 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6afb9c0
   |   |   )id_expression#6afbaa0
   |   |  )declarator_id#6afbb80
   |   | )noptr_declarator#6afbba0
   |   |)ptr_declarator#6afbc40
   |   )parameter_declaration#6afbd40
   |  )template_parameter#6afbd80
   | )template_parameter_list#6afbde0
   | (declaration@Cpp~GCC5=1033#6b04940^1#6b04960:2 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |  (block_declaration@Cpp~GCC5=1050#6b04920^1#6b04940:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   (simple_declaration@Cpp~GCC5=1060#6b04900^1#6b04920:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b048e0^1#6b04900:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b048c0^1#6b048e0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |  (type_specifier@Cpp~GCC5=1110#6b048a0^1#6b048c0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   (class_specifier@Cpp~GCC5=1761#6b04880^1#6b048a0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   |(class_head@Cpp~GCC5=1763#6afb980^1#6b04880:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   | (class_key@Cpp~GCC5=1791#6afbca0^1#6afb980:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp)class_key
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afbcc0^1#6afb980:2[`answer'] Line 1 Column 25 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | (optional_base_clause@Cpp~GCC5=1872#6afba60^1#6afb980:3 Line 1 Column 32 File C:/temp/prime_with_templates.cpp)optional_base_clause
   |   |   |)class_head#6afb980
   |   |   |(member_specification@Cpp~GCC5=1794#6b042e0^1#6b04880:2 {2} Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b04060^1#6b042e0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04040^1#6b04060:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04020^1#6b04040:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1673#6afbec0^1#6b04020:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6afbfe0^1#6afbec0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6afbf80^1#6afbfe0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6afbf60^1#6afbf80:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6afbea0^1#6afbf60:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6afbb40^1#6afbea0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6afbc80^1#6afbb40:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6afbc20^1#6afbc80:1[`answer'] Line 1 Column 34 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |  )unqualified_id#6afbc80
   |   |   |   |   | )id_expression#6afbb40
   |   |   |   |   |)declarator_id#6afbea0
   |   |   |   |   )noptr_declarator#6afbf60
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1559#6afbd00^1#6afbf80:2 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(pp_parameter_declaration_list@Cpp~GCC5=1570#6afb940^1#6afbd00:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pp_parameter_declaration_seq@Cpp~GCC5=1574#6afb800^1#6afb940:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (parameter_declaration@Cpp~GCC5=1610#6afb9a0^1#6afb800:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6afbf40^1#6afb9a0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(decl_specifier@Cpp~GCC5=1073#6afbfa0^1#6afbf40:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6afbfc0^1#6afbfa0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (simple_type_specifier@Cpp~GCC5=1140#6afb860^1#6afbfc0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   |   | )trailing_type_specifier#6afbfc0
   |   |   |   |   |   |)decl_specifier#6afbfa0
   |   |   |   |   |   )basic_decl_specifier_seq#6afbf40
   |   |   |   |   |  )parameter_declaration#6afb9a0
   |   |   |   |   | )pp_parameter_declaration_seq#6afb800
   |   |   |   |   |)pp_parameter_declaration_list#6afb940
   |   |   |   |   )parameter_declaration_clause#6afbd00
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6afbce0^1#6afbf80:3 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6afbf80
   |   |   |   | )ptr_declarator#6afbfe0
   |   |   |   |)function_head#6afbec0
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04000^1#6b04020:2 Line 1 Column 46 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=888#6afbee0^1#6b04000:1 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)compound_statement
   |   |   |   |)function_body#6b04000
   |   |   |   )function_definition#6b04020
   |   |   |  )member_declaration#6b04040
   |   |   | )member_declaration_or_access_specifier#6b04060
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b042c0^1#6b042e0:2 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04820^1#6b042c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04280^1#6b04820:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1674#6b04220^1#6b04280:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b040e0^1#6b04220:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (decl_specifier@Cpp~GCC5=1073#6b040c0^1#6b040e0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b040a0^1#6b040c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(simple_type_specifier@Cpp~GCC5=1138#6b04080^1#6b040a0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   )trailing_type_specifier#6b040a0
   |   |   |   |  )decl_specifier#6b040c0
   |   |   |   | )basic_decl_specifier_seq#6b040e0
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6b04200^1#6b04220:2 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6b041e0^1#6b04200:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6b041a0^1#6b041e0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6b04180^1#6b041a0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b04160^1#6b04180:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=320#6b04140^1#6b04160:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (operator_function_id@Cpp~GCC5=2027#6b04120^1#6b04140:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(operator@Cpp~GCC5=2070#6b04100^1#6b04120:1 Line 1 Column 62 File C:/temp/prime_with_templates.cpp)operator
   |   |   |   |   |   )operator_function_id#6b04120
   |   |   |   |   |  )unqualified_id#6b04140
   |   |   |   |   | )id_expression#6b04160
   |   |   |   |   |)declarator_id#6b04180
   |   |   |   |   )noptr_declarator#6b041a0
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1558#6afba40^1#6b041e0:2 Line 1 Column 65 File C:/temp/prime_with_templates.cpp)parameter_declaration_clause
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6b041c0^1#6b041e0:3 Line 1 Column 66 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6b041e0
   |   |   |   | )ptr_declarator#6b04200
   |   |   |   |)function_head#6b04220
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04300^1#6b04280:2 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=889#6b04760^1#6b04300:1 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (pp_statement_seq@Cpp~GCC5=894#6b04780^1#6b04760:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (statement@Cpp~GCC5=857#6b04440^1#6b04780:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(jump_statement@Cpp~GCC5=1011#6afba80^1#6b04440:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pm_expression@Cpp~GCC5=551#6b04380^1#6afba80:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (cast_expression@Cpp~GCC5=543#6b04360^1#6b04380:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (unary_expression@Cpp~GCC5=465#6b04340^1#6b04360:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(primary_expression@Cpp~GCC5=307#6b04320^1#6b04340:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b042a0^1#6b04320:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04260^1#6b042a0:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04240^1#6b04260:1[`V'] Line 1 Column 74 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |   |  )unqualified_id#6b04260
   |   |   |   |   |   | )id_expression#6b042a0
   |   |   |   |   |   |)primary_expression#6b04320
   |   |   |   |   |   )unary_expression#6b04340
   |   |   |   |   |  )cast_expression#6b04360
   |   |   |   |   | )pm_expression#6b04380
   |   |   |   |   |)jump_statement#6afba80
   |   |   |   |   )statement#6b04440
   |   |   |   |  )pp_statement_seq#6b04780
   |   |   |   | )compound_statement#6b04760
   |   |   |   |)function_body#6b04300
   |   |   |   )function_definition#6b04280
   |   |   |  )member_declaration#6b04820
   |   |   | )member_declaration_or_access_specifier#6b042c0
   |   |   |)member_specification#6b042e0
   |   |   )class_specifier#6b04880
   |   |  )type_specifier#6b048a0
   |   | )decl_specifier#6b048c0
   |   |)basic_decl_specifier_seq#6b048e0
   |   )simple_declaration#6b04900
   |  )block_declaration#6b04920
   | )declaration#6b04940
   |)template_declaration#6b04960
   )declaration#6b04980
  )pp_declaration_seq#6b049a0
  (pp_declaration_seq@Cpp~GCC5=1022#6b06620^1#6b06640:2 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b06600^1#6b06620:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b065e0^1#6b06600:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2083#6b05460^1#6b065e0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter_list@Cpp~GCC5=2083#6b05140^1#6b05460:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   (template_parameter_list@Cpp~GCC5=2083#6b04ee0^1#6b05140:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(template_parameter_list@Cpp~GCC5=2082#6b04cc0^1#6b04ee0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (template_parameter@Cpp~GCC5=2085#6b04ca0^1#6b04cc0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (parameter_declaration@Cpp~GCC5=1611#6b04c80^1#6b04ca0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04a40^1#6b04c80:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |(decl_specifier@Cpp~GCC5=1073#6b04a20^1#6b04a40:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6b04a00^1#6b04a20:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |  (simple_type_specifier@Cpp~GCC5=1138#6b049e0^1#6b04a00:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   | )trailing_type_specifier#6b04a00
   |   |   |)decl_specifier#6b04a20
   |   |   )basic_decl_specifier_seq#6b04a40
   |   |   (ptr_declarator@Cpp~GCC5=1417#6b04c40^1#6b04c80:2 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(noptr_declarator@Cpp~GCC5=1421#6b04be0^1#6b04c40:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (declarator_id@Cpp~GCC5=1487#6b04bc0^1#6b04be0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |  (id_expression@Cpp~GCC5=317#6b04b60^1#6b04bc0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   (unqualified_id@Cpp~GCC5=319#6b04ac0^1#6b04b60:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   |(IDENTIFIER@Cpp~GCC5=3368#6b049c0^1#6b04ac0:1[`no'] Line 3 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   )unqualified_id#6b04ac0
   |   |   |  )id_expression#6b04b60
   |   |   | )declarator_id#6b04bc0
   |   |   |)noptr_declarator#6b04be0
   |   |   )ptr_declarator#6b04c40
   |   |  )parameter_declaration#6b04c80
   |   | )template_parameter#6b04ca0
   |   |)template_parameter_list#6b04cc0
   |   |(template_parameter@Cpp~GCC5=2085#6b04ec0^1#6b04ee0:2 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   | (parameter_declaration@Cpp~GCC5=1611#6b04ea0^1#6b04ec0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |  (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04b40^1#6b04ea0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   (decl_specifier@Cpp~GCC5=1073#6b04ba0^1#6b04b40:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   |(trailing_type_specifier@Cpp~GCC5=1118#6b04c60^1#6b04ba0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   | (simple_type_specifier@Cpp~GCC5=1138#6b04580^1#6b04c60:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |)trailing_type_specifier#6b04c60
   |   |   )decl_specifier#6b04ba0
   |   |  )basic_decl_specifier_seq#6b04b40
   |   |  (ptr_declarator@Cpp~GCC5=1417#6b04e60^1#6b04ea0:2 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   (noptr_declarator@Cpp~GCC5=1421#6b04e40^1#6b04e60:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |(declarator_id@Cpp~GCC5=1487#6b04de0^1#6b04e40:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   | (id_expression@Cpp~GCC5=317#6b04d80^1#6b04de0:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04ce0^1#6b04d80:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04560^1#6b04ce0:1[`yes'] Line 3 Column 24 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |  )unqualified_id#6b04ce0
   |   |   | )id_expression#6b04d80
   |   |   |)declarator_id#6b04de0
   |   |   )noptr_declarator#6b04e40
   |   |  )ptr_declarator#6b04e60
   |   | )parameter_declaration#6b04ea0
   |   |)template_parameter#6b04ec0
   |   )template_parameter_list#6b04ee0
   |   (template_parameter@Cpp~GCC5=2085#6b05120^1#6b05140:2 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |(parameter_declaration@Cpp~GCC5=1611#6b05100^1#6b05120:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04d20^1#6b05100:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |  (decl_specifier@Cpp~GCC5=1073#6b04dc0^1#6b04d20:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b04e80^1#6b04dc0:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   |(simple_type_specifier@Cpp~GCC5=1140#6b046e0^1#6b04e80:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   )trailing_type_specifier#6b04e80
   |   |  )decl_specifier#6b04dc0
   |   | )basic_decl_specifier_seq#6b04d20
   |   | (ptr_declarator@Cpp~GCC5=1417#6b05080^1#6b05100:2 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |  (noptr_declarator@Cpp~GCC5=1421#6b05020^1#6b05080:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   (declarator_id@Cpp~GCC5=1487#6b05000^1#6b05020:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |(id_expression@Cpp~GCC5=317#6b04fa0^1#6b05000:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   | (unqualified_id@Cpp~GCC5=319#6b04f00^1#6b04fa0:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |  (IDENTIFIER@Cpp~GCC5=3368#6b046c0^1#6b04f00:1[`f'] Line 3 Column 33 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | )unqualified_id#6b04f00
   |   |   |)id_expression#6b04fa0
   |   |   )declarator_id#6b05000
   |   |  )noptr_declarator#6b05020
   |   | )ptr_declarator#6b05080
   |   |)parameter_declaration#6b05100
   |   )template_parameter#6b05120
   |  )template_parameter_list#6b05140
   |  (template_parameter@Cpp~GCC5=2085#6b05440^1#6b05460:2 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6b05420^1#6b05440:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b05160^1#6b05420:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b04fe0^1#6b05160:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6b050e0^1#6b04fe0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1140#6b050c0^1#6b050e0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6b050e0
   |   | )decl_specifier#6b04fe0
   |   |)basic_decl_specifier_seq#6b05160
   |   |(ptr_declarator@Cpp~GCC5=1417#6b053e0^1#6b05420:2 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6b053c0^1#6b053e0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6b05360^1#6b053c0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6b05280^1#6b05360:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6b051a0^1#6b05280:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6b046a0^1#6b051a0:1[`p'] Line 3 Column 40 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6b051a0
   |   |   )id_expression#6b05280
   |   |  )declarator_id#6b05360
   |   | )noptr_declarator#6b053c0
   |   |)ptr_declarator#6b053e0
   |   )parameter_declaration#6b05420
   |  )template_parameter#6b05440
   | )template_parameter_list#6b05460

Визначення, чи є це змінною декларацією чи множенням, не є функцією перевірки типу. Також мені довелося скрупулювати вашу відповідь на ті речі з самореклами ... ще раз.
Щеня

@Puppy: ви можете сказати, що вам подобається, але саме так інструмент працює. Видалення імені інструменту, ймовірно, просто змусить людей запитати, що таке ім'я інструменту.
Іра Бакстер

Незалежно від того, чи працює цей інструмент, не має значення, оскільки питання не задає роботу інструменту. Крім того, я думаю, що ми можемо сміливо чекати, коли це насправді станеться.
Щеня
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.