Среди многих вещей, которым научил меня переполнение стека, является то, что известно как «самый неприятный синтаксический анализ», что классически демонстрируется такой строкой, как
A a(B()); //declares a function
Хотя для большинства это интуитивно кажется объявлением объекта a
типа A
, принимая временный B
объект в качестве параметра конструктора, на самом деле это объявление функции, a
возвращающей A
, принимающей указатель на функцию, которая возвращает B
и сама не принимает никаких параметров , Точно так же линия
A a(); //declares a function
также подпадает под ту же категорию, так как вместо объекта он объявляет функцию. Теперь, в первом случае, обычным решением этой проблемы является добавление дополнительного набора скобок / круглых скобок вокруг B()
, так как компилятор будет интерпретировать его как объявление объекта.
A a((B())); //declares an object
Тем не менее, во втором случае, то же самое приводит к ошибке компиляции
A a(()); //compile error
У меня вопрос, почему? Да, я очень хорошо знаю, что правильный «обходной путь» - это изменить его на A a;
, но мне любопытно узнать, что ()
делает extra в компиляторе в первом примере, который затем не работает при его повторном применении в второй пример. Является ли A a((B()));
обходной путь конкретным исключением, записанным в стандарте?
(B())
это просто выражение C ++, не более того. Это не исключение. Единственное отличие, которое он имеет, состоит в том, что его невозможно проанализировать как тип, и это не так.A a();
относится к той же категории. Для компилятора никогда не существует другого способа его анализа: инициализатор в этом месте никогда не состоит из пустых скобок, поэтому это всегда объявление функции.A a();
это не пример самого неприятного разбора . Это просто объявление функции, как в C.A a;
« неправильный ». Это не даст вам инициализацию типа POD. Чтобы получить инициализацию пишитеA a{};
.Ответы:
Просветленного ответа нет, просто потому, что он не определен как допустимый синтаксис языком C ++ ... Так оно и есть, по определению языка.
Если у вас есть выражение внутри, то оно действительно. Например:
Проще говоря: потому что
(x)
это правильное выражение C ++, а()
нет.Чтобы узнать больше о том, как определяются языки и как работают компиляторы, вам следует изучить теорию формальных языков или, в частности, контекстно-свободные грамматики (CFG) и связанные с ними материалы, такие как конечные автоматы. Если вам это интересно, хотя страниц википедии будет недостаточно, вам нужно будет получить книгу.
источник
(x)
это правильное выражение C ++, а()
нет.Окончательное решение этой проблемы - перейти на единый синтаксис инициализации C + 11, если вы можете.
http://www.stroustrup.com/C++11FAQ.html#uniform-init
источник
Описатели функции C
Прежде всего, есть C. В C,
A a()
это объявление функции. Например,putchar
имеет следующую декларацию. Обычно такие объявления хранятся в заголовочных файлах, однако ничто не мешает вам писать их вручную, если вы знаете, как выглядит объявление функции. Имена аргументов являются необязательными в объявлениях, поэтому в этом примере я опустил их.Это позволяет вам писать код следующим образом.
C также позволяет вам определять функции, которые принимают функции в качестве аргументов, с хорошим читаемым синтаксисом, который выглядит как вызов функции (ну, это читабельно, пока вы не вернете указатель на функцию).
Как я уже говорил, C позволяет опускать имена аргументов в заголовочных файлах, поэтому в заголовочном файле они
output_result
будут выглядеть так.Один аргумент в конструкторе
Разве вы не узнаете это? Хорошо, позвольте мне напомнить вам.
Да, это точно такое же объявление функции.
A
естьint
,a
естьoutput_result
иB
естьint
.Вы можете легко заметить конфликт C с новыми функциями C ++. Чтобы быть точным, конструкторы, являющиеся именем класса и круглыми скобками, и альтернативный синтаксис объявления с
()
вместо=
. По своей природе C ++ пытается быть совместимым с кодом C, и поэтому ему приходится иметь дело с этим случаем - даже если это практически никого не волнует. Поэтому старые функции C имеют приоритет перед новыми функциями C ++. Грамматика объявлений пытается сопоставить имя как функцию, прежде чем вернуться к новому синтаксису с()
ошибкой.Если бы одна из этих функций не существовала или имела другой синтаксис (как
{}
в C ++ 11), эта проблема никогда бы не возникла для синтаксиса с одним аргументом.Теперь вы можете спросить, почему
A a((B()))
работает. Что ж, давайте объявимoutput_result
с бесполезными скобками.Это не сработает. Грамматика требует, чтобы переменная не была в скобках.
Тем не менее, C ++ ожидает стандартное выражение здесь. В C ++ вы можете написать следующий код.
И следующий код.
C ++ ожидает, что выражение внутри круглых скобок будет ... ну ... выражением, в отличие от ожидаемого типа C. Скобки здесь ничего не значат. Однако, вставляя бесполезные скобки, объявление функции C не совпадает, и новый синтаксис может быть сопоставлен должным образом (который просто ожидает выражение, например
2 + 2
).Больше аргументов в конструкторе
Конечно, один аргумент хорош, но как насчет двух? Дело не в том, что у конструкторов может быть только один аргумент. Один из встроенных классов, который принимает два аргумента
std::string
Это все хорошо (технически, у него будет самый неприятный синтаксический анализ, если он будет записан как
std::string wat(int(), char())
, но давайте будем честными - кто бы это написал? Но давайте предположим, что у этого кода есть неприятная проблема. Вы могли бы предположить, что вы должны поставить все в скобках.Не совсем так.
Я не уверен, почему g ++ пытается конвертировать
char
вconst char *
. В любом случае, конструктор вызывался только с одним значением типаchar
. Не существует перегрузки, которая имеет один аргумент типаchar
, поэтому компилятор сбит с толку. Вы можете спросить - почему аргумент имеет тип char?Да,
,
здесь оператор запятой. Оператор запятой принимает два аргумента и дает аргумент справа. Это не очень полезно, но это то, что нужно знать по моим объяснениям.Вместо этого, чтобы решить самый неприятный анализ, необходим следующий код.
Аргументы указаны в скобках, а не во всем выражении. Фактически, только одно из выражений должно быть в скобках, так как достаточно немного отойти от грамматики C, чтобы использовать функцию C ++. Все сводит нас к нулю аргументов.
Нулевые аргументы в конструкторе
Возможно, вы заметили
eighty_four
функцию в моем объяснении.Да, это также затронуто самым неприятным анализом. Это правильное определение, которое вы, скорее всего, видели, если создавали заголовочные файлы (и вам следует). Добавление скобок не исправляет это.
Почему это так? Ну,
()
это не выражение. В C ++ вы должны поместить выражение в скобки. Вы не можете писатьauto value = ()
на C ++, потому что()
ничего не значит (и даже если бы сделал, как пустой кортеж (см. Python), это был бы один аргумент, а не ноль). Практически это означает, что вы не можете использовать сокращенный синтаксис без использования синтаксиса C ++ 11{}
, так как в скобках нет выражений, а грамматика C для объявлений функций всегда будет применяться.источник
Вы могли бы вместо
использование
источник
int a = int();
инициализируетсяa
0,int a;
оставляетa
неинициализированным. Правильный обходной путь - использоватьA a = {};
для агрегатов,A a;
когда инициализация по умолчанию делает то, что вы хотите, аA a = A();
во всех других случаях - или просто использоватьA a = A();
последовательно. В C ++ 11 просто используйтеA a {};
Самым внутренним паренем в вашем примере было бы выражение, а в C ++ грамматика определяет, что
expression
должен бытьassignment-expression
или другой,expression
за которым следует запятая и другаяassignment-expression
(Приложение A.4 - Сводка / выражения грамматики).Грамматика далее определяет
assignment-expression
как один из нескольких других типов выражений, ни один из которых не может быть ничем (или только пробелом).Поэтому причина, по которой вы не можете этого сделать,
A a(())
заключается просто в том, что грамматика не позволяет этого. Тем не менее, я не могу ответить, почему люди, создавшие C ++, не допускали такого особого использования пустых паренов в качестве какого-то особого случая - я предполагаю, что они бы не стали помещать такой особый случай, если бы был разумная альтернатива.источник