Я не понимаю, почему это компилирует

80

Я, конечно, что-то упускаю, но я не понимаю, почему это компилируется (как с g ++, так и clang ++):

struct A
{
};
struct B
{
};

int main()
{
  A a(B);
}

Прежде всего, Bэто тип ... не значение. Как я должен интерпретировать этот код?

Пикауд Винсент
источник
37
Это известно как самый неприятный разбор
alter igel
8
@alterigel Это правда? В этом случае нет никакой двусмысленности. Это может быть только объявление функции. Это не A a(B());может быть определение переменной или объявление функции.
грецкий орех
8
Вы были бы удивлены, узнав, что struct A{}; int main() { A(foo); } компилируется как есть , даже если fooничего не называется.
Ayxan
20
@alterigel - это не самый неприятный разбор. Посмотрите на примеры на странице, на которую вы ссылались. Это просто объявление функции.
Пит Беккер
3
@PeteBecker, возможно, было бы лучше объяснить, почему это не MVP, вместо того, чтобы просто утверждать, что это не так, как я полагаю, грецкий орех уже делал выше.
JPhi1618

Ответы:

84

Это интерпретируется как объявление именованной функции a, которая принимает один аргумент типа Bи возвращает A.

Брайан
источник
5
И именно поэтому это Мост и Вексинг. Решение: (не то, чтобы оно действительно что-то A a{B};
решало,
23
@ user4581301 - это не самый неприятный разбор. Это просто объявление функции.
Пит Беккер
23
Так что получается, что это в основном неприятный анализ ...
MooseBoys
11
Часть странной об этом в том , что C ++ не допускает вложенные функции, но это позволяет декларацию внутри функции.
The_Sympathizer
6
Звучит как хорошая мотивация для добавления поддержки вложенных функций в C ++; они не только пригодятся, но и превратят эту странную бородавку в разумный дизайн :)
Джереми
15

Это просто объявление функции, объявляющее aфункцию, возвращающую Aи принимающую один безымянный параметр типа B.

Это допустимо, потому что объявления функций в отличие от определений функций допускаются в определениях функций.

machine_1
источник
13

Эта проблема известна как самый неприятный анализ . Строка A a(B);может быть интерпретирована как объявление функции с именем, aвозвращающей объект типа Aи принимающей безымянный параметр типа B.

Одним из способов избежать этой проблемы является использование единого синтаксиса инициализации, который был введен в C ++ 11, который состоит в использовании фигурных скобок вместо скобок: A a{B};возвращает ошибку. Строка теперь интерпретируется как объявление переменной B, которая инициализируется с помощью типа, а не значения.

Вот больше информации:

Самый неприятный парсинг: как это быстро определить и исправить

Г. Морен
источник
12
Я не думаю, что это следует называть « самым неприятным разбором ». Это просто обычное объявление функции, так как оно также существует в C. Нет необходимости в разрешении неоднозначности, потому что строка может быть только объявлением функции, ничем иным. Посмотрите на вашу ссылку. Все примеры отличаются от этого.
грецкий орех
3
Хотя это правда, это связано с самым неприятным разбором. Просто это также включало опечатку, где имя типа использовалось отдельно вместо переменной или вызова конструктора, как, возможно, было первоначальное намерение.
Мирал
1
Да, «Most Vexing Parse» является полезным ответом в этом случае, даже если в данном вопросе речь идет просто «Немного Vexing Parse».
JPA
1
@wlanut: пустые структуры struct A { };недопустимы в стандартном C, даже если некоторые компиляторы разрешают их. Снимите скобки, и там не будет проблем. Кроме того, в C декларирование или определение struct Aне создает имя типа A(вы должны префиксировать его structили добавить typedef struct A A;куда-либо, прежде чем Aиспользовать без structпрефикса). Кроме того, в C нет альтернативного синтаксического анализа объявления функции - использование type name(...);просто не может быть определением переменной; это всегда объявление функции (или недействительное). Код в вопросе недействителен в C.
Джонатан Леффлер