Ответ 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:
Неоднозначность является чисто синтаксической; то есть значение имен, встречающихся в таком утверждении, вне зависимости от того, являются ли они именами типов или нет, обычно не используется в неоднозначности или не изменяется.
[...]
Устранение неоднозначности предшествует синтаксическому анализу, и утверждение, неоднозначное как декларация, может быть неправильно сформированной декларацией.
Исправлено в редактировании: я пропустил рассматриваемое объявление, находящееся в другой области видимости, чем параметр функции, и поэтому объявление было правильно сформировано, если был доступен конструктор. Это не учитывается при устранении неоднозначности в любом случае. Также раскрыты некоторые детали.