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