This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 117a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2025-06-16
Consider:
int x = (int()) + 5;
This is ill-formed, because 9.3.3 [dcl.ambig.res] paragraph 2 specifies:
An ambiguity can arise from the similarity between a function-style cast and a type-id. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.
and thus int() is interpreted as a type-id instead of as a function-style cast, so this is an ill-formed cast to a function type.
This seems to be the wrong disambiguation for all cases where there is a choice between a C-style cast and a parenthesized expression: in all those cases, the C-style cast interpretation results in a cast to a function type, which is always ill-formed.
Further, there is implementation divergence in the handling of this example:
struct T { int operator++(int); T operator[](int); };
int a = (T()[3])++; // not a cast
EWG 2022-11-11
This is tracked in github issue cplusplus/papers#1376.
Additional notes (May, 2025)
Also consider this example:
(S())[]->A<int>; // OK, constructor call (S())[]->A<int> {return {};}; // error: C-style cast of lambda
Without the suggested simple rule that (T()) is never a conversion to a function type (which is always semantically ill-formed), syntactic disambiguation would require analysis whether the thing after the arrow is a type (i.e. a trailing return type) or an expression (for operator->).
Suggested resolution (2019-01-02):
Change in 7.6.2 [expr.unary] paragraph 1 as follows:
unary-expression : postfix-expression unary-operator cast-expression ++ cast-expression -- cast-expression await-expression sizeof unary-expression sizeof ( nofun-type-id ) sizeof ... ( identifier ) alignof ( nofun-type-id ) noexcept-expression new-expression delete-expression
Change in 7.6.2.5 [expr.sizeof] paragraph 1 as follows:
The operand is either an expression, which is an unevaluated operand (7.2.3 [expr.context]), or a parenthesized nofun-type-id.
Change in 7.6.2.6 [expr.alignof] paragraph 1 as follows:
The operand shall be a nofun-type-id representing a complete object type, or an array thereof, or a reference to one of those types.
Replace type-id with nofun-type-id throughout 7.6.2.8 [expr.new], for example in paragraph 1:
new-expression : ::opt new new-placementopt new-type-id new-initializeropt ::opt new new-placementopt ( nofun-type-id ) new-initializeropt
Change in 7.6.3 [expr.cast] paragraph 2 as follows:
cast-expression : unary-expression ( nofun-type-id ) cast-expression
Change in 9.2.9.7.1 [dcl.spec.auto.general] paragraph 6 as follows:
A placeholder type can also be used in the type-specifier-seq of the new-type-id or in the nofun-type-id of a new-expression (7.6.2.8 [expr.new]). In such a nofun-type-id, the placeholder type shall appear as one of the type-specifiers in the type-specifier-seq or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a type-specifier .
Change in 9.2.9.7.2 [dcl.type.auto.deduct] paragraph 2 as follows:
A placeholder for a deduced class type can also be used in the type-specifier-seq in the new-type-id or nofun-type-id of a new-expression (7.6.2.8 [expr.new]), as the simple-type-specifier in an explicit type conversion (functional notation) (7.6.1.4 [expr.type.conv]), or as the type-specifier in the parameter-declaration of a template-parameter (13.2 [temp.param]). A placeholder for a deduced class type shall not appear in any other context.
Change in 9.3.1 [dcl.decl.general] paragraph 6 as follows, to fix auto (*p)() -> int(X()); (now a function pointer initialized by X()):
trailing-return-type : -> nofun-type-id
Change in 9.3.2 [dcl.name] paragraph 1 as follows:
nofun-type-id: type-specifier-seq nofun-declaratoropt nofun-declarator: ptr-nofun-declarator noptr-nofun-declarator parameters-and-qualifiers trailing-return-type ptr-nofun-declarator: noptr-nofun-declarator ptr-operator ptr-nofun-declaratoropt noptr-nofun-declarator: noptr-nofun-declarator parameters-and-qualifiers noptr-nofun-declaratoropt [ constant-expressionopt ] attribute-specifier-seqopt ( ptr-nofun-declarator )It is possible to identify uniquely the location in the abstract-declarator or nofun-declarator where the identifier would appear if the construction were a declarator in a declaration. The named type is then the same as the type of the hypothetical identifier.
Change in 9.3.3 [dcl.ambig.res] paragraph 2 as follows:
An ambiguity can arise from the similarity between a function-style cast and a type-id. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id. [ Note: No such ambiguity can arise between an expression and a nofun-type-id. -- end note ] However, a construct that can syntactically be a type-id whose outermost abstract-declarator would match the grammar of an abstract-declarator with a trailing-return-type is considered a type-id only if it starts with auto. [Example 2 :template <class T> struct X {}; template <int N> struct Y {}; X<int()> a; // type-id X<int(1)> b; // expression (ill-formed) Y<int()> c; // type-id (ill-formed) Y<int(1)> d; // expression void foo(signed char a) { sizeof(int()); //-- end example ]type-id (ill-formed)expression sizeof(int(a)); // expression sizeof(int(unsigned(a))); //type-id (ill-formed)expression (int())+1; //type-id (ill-formed)expression (int(a))+1; // expression (int(unsigned(a)))+1; //type-id (ill-formed)expression } typedef struct BB { int C[2]; } *B, C; void g() { sizeof(B()->C[1]); // OK, sizeof(expression) sizeof(auto()->C[1]); // error: sizeof of a function returning an array }
Change in 9.13.1 [dcl.attr.grammar] paragraph 1 as follows:
alignment-specifier : alignas ( nofun-type-id ...opt ) alignas ( constant-expression ...opt )
Change in 9.13.2 [dcl.align] paragraph 3 as follows:
An alignment-specifier of the form alignas( nofun-type-id ) has the same effect as alignas(alignof( nofun-type-id )) (7.6.2.6 [expr.alignof]).
Do not change 13.8.1 [temp.res.general] paragraph 4.
Change in 13.8.3.3 [temp.dep.expr] paragraph 3 as follows:
Expressions of the following forms are type-dependent only if the type specified by the type-id, nofun-type-id, simple-type-specifier, typename-specifier, or new-type-id is dependent, even if any subexpression is type-dependent:simple-type-specifier ( expression-listopt ) simple-type-specifier braced-init-list typename-specifier ( expression-listopt ) typename-specifier braced-init-list ::opt new new-placementopt new-type-id new-initializeropt ::opt new new-placementopt ( nofun-type-id ) new-initializeropt dynamic_cast < type-id > ( expression ) static_cast < type-id > ( expression ) const_cast < type-id > ( expression ) reinterpret_cast < type-id > ( expression ) ( nofun-type-id ) cast-expression
Change in 13.8.3.3 [temp.dep.expr] paragraph 4 as follows:
literal sizeof unary-expression sizeof ( nofun-type-id ) sizeof ... ( identifier ) alignof ( nofun-type-id ) typeid ( expression ) typeid ( type-id ) ::opt delete cast-expression ::opt delete [ ] cast-expression throw assignment-expressionopt noexcept ( expression ) requires-expression
Change in 13.8.3.4 [temp.dep.constexpr] paragraph 2 as follows:
Expressions of the following form are value-dependent if the unary-expression or expression is type-dependent or the type-id or nofun-type-id is dependent:sizeof unary-expression sizeof ( nofun-type-id ) typeid ( expression ) typeid ( type-id ) alignof ( nofun-type-id )
Change in 13.8.3.4 [temp.dep.constexpr] paragraph 3 as follows:
Expressions of the following form are value-dependent if either the type-id, nofun-type-id, simple-type-specifier, or typename-specifier is dependent or the expression or cast-expression is value-dependent or any expression in the expression-list is value-dependent or any assignment-expression in the braced-init-list is value-dependent:simple-type-specifier ( expression-listopt ) typename-specifier ( expression-listopt ) simple-type-specifier braced-init-list typename-specifier braced-init-list static_cast < type-id > ( expression ) const_cast < type-id > ( expression ) reinterpret_cast < type-id > ( expression ) dynamic_cast < type-id > ( expression ) ( nofun-type-id ) cast-expression
EWG 2025-06-16
EWG agrees with the direction represented by the wording for the May, 2025 notes, above.