Created on 2016-02-02.00:00:00 last changed 4 months ago
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()); // 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
}
-- end example ]
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.
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->).
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.
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2025-06-12 22:07:03 | admin | set | messages: + msg8034 |
| 2025-06-12 22:07:03 | admin | set | status: open -> review |
| 2022-11-10 15:00:26 | admin | set | status: review -> open |
| 2020-12-15 00:00:00 | admin | set | messages: + msg6222 |
| 2020-12-15 00:00:00 | admin | set | status: drafting -> review |
| 2018-02-27 00:00:00 | admin | set | status: open -> drafting |
| 2016-02-02 00:00:00 | admin | create | |