Ответ 1
Clang выдает это предупреждение:
<source>:12:16: warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'num' [-Wvexing-parse]
Boo(num); // No default constructor
^~~~~
Это самая неприятная проблема разбора. Поскольку Boo
- это имя типа класса, а num
- это не имя типа, Boo(num);
это может быть либо конструкция временного типа Boo
с аргументом num
для конструктора Boo
либо это может быть объявление Boo num;
с дополнительными скобками вокруг декларатора num
(который всегда может иметь декларатор). Если оба являются допустимыми интерпретациями, стандарт требует, чтобы компилятор принял объявление.
Если он анализируется как объявление, то Boo num;
вызовет конструктор по умолчанию (конструктор без аргументов), который не объявлен ни вами, ни неявно (потому что вы объявили другой конструктор). Поэтому программа плохо сформирована.
Это не проблема с Boo(8);
потому что 8
не может быть идентификатором переменной (декларатор-идентификатор), поэтому он анализируется как вызов, создающий Boo
временный с 8
качестве аргумента для конструктора, тем самым не вызывая конструктор по умолчанию (который не объявлен), но тот, который вы определяется вручную.
Вы можете избавиться от неоднозначности этого из объявления, используя Boo{num};
вместо Boo(num);
(потому что {}
вокруг объявления не допускается), делая временную переменную именованной, например Boo temp(num);
или путем помещения его в качестве операнда в другое выражение, например (Boo(num));
, (void)Boo(num);
, так далее.
Обратите внимание, что объявление было бы правильно сформировано, если бы был использован конструктор по умолчанию, потому что оно находится внутри области действия ветки if
а не в области функциональных блоков и просто затеняет num
в списке параметров функции.
В любом случае не очень хорошая идея неправильно использовать создание временных объектов для чего-то, что должно быть нормальным вызовом функции (члена).
Этот конкретный тип наиболее неприятного анализа с одним не типичным именем в круглых скобках может произойти только потому, что целью является создание временного объекта и немедленное его удаление, или, в качестве альтернативы, если это намерение создать временный объект, используемый непосредственно в качестве инициализатора, например Boo boo(Boo(num));
(фактически объявляет функцию boo
принимающую параметр с именем num
с типом Boo
и возвращающую Boo
).
Немедленное отбрасывание временных значений обычно не предназначено, и можно избежать случая инициализатора, используя инициализацию скобок или двойные паразиты (Boo boo{Boo(num)}
, Boo boo(Boo{num})
или Boo boo((Boo(num)));
но не Boo boo(Boo((num)));
).
Если Boo
не является именем типа, оно не может быть объявлением, и никаких проблем не возникает.
Я также хочу подчеркнуть, что Boo(8);
создает новый временный объект типа Boo
даже внутри области видимости класса и определения конструктора. Это не, как можно ошибочно подумать, вызов конструктора с вызывающим this
указателем, как для обычных нестатических функций-членов. Невозможно вызвать другой конструктор таким образом внутри тела конструктора. Это возможно только в списке инициализатора члена конструктора.
Это происходит, даже если объявление будет неправильно сформировано из-за отсутствия конструктора из-за [stmt.ambig]/3:
Неоднозначность является чисто синтаксической; то есть значение имен, встречающихся в таком утверждении, вне зависимости от того, являются ли они именами типов или нет, обычно не используется в неоднозначности или не изменяется.
[...]
Устранение неоднозначности предшествует синтаксическому анализу, и утверждение, неоднозначное как декларация, может быть неправильно сформированной декларацией.
Исправлено в редактировании: я пропустил рассматриваемое объявление, находящееся в другой области видимости, чем параметр функции, и поэтому объявление было правильно сформировано, если был доступен конструктор. Это не учитывается при устранении неоднозначности в любом случае. Также раскрыты некоторые детали.