There is one way to write this expression without some sort of type alias. But it's not worth it.
The grammar for new-expression is
new-expression:
::
opt new-placementopt new-type-id new-initializeropt
::
opt new-placementopt (
type-id)
new-initializeropt
new-type-id:
type-specifier-seq new-declaratoropt
new-declarator:
ptr-operator new-declaratoropt
noptr-new-declarator
ptr-operator:
*
attribute-specifier-seqopt cv-qualifier-seqopt
&
attribute-specifier-seqopt
&&
attribute-specifier-seqopt
nested-name-specifier *
attribute-specifier-seqopt cv-qualifier-seqopt
noptr-new-declarator:
[
expression ]
attribute-specifier-seqopt
noptr-new-declarator [
constant-expression ]
attribute-specifier-seqopt
The new-declarator grammar symbol used in new-type-id is like the abstract-declarator symbol used in type-id, with three important differences:
new-declarator allows a non-constant expression between the first set of square brackets, to specify dynamic creation of an array with variable size. type-id does not because "array T
of size N
" is only a valid C++ type if N
is a constant expression.
type-id allows enclosing a ptr-abstract-declarator in parentheses to help specify a pointer-to-array or pointer-to-function type, and/or enclosing a function parameter list in parentheses to specify a function type. new-declarator does not allow any parentheses at all! Any parentheses following a new
keyword specify either a new-placement, or a parenthesized type-id, or a new-initializer. (I guess if new-type-id could also involve parentheses, disambiguating the combinations of all these things would be extremely messy.)
type-id allows the ...
ellipsis token in the place where a real declaration would have an identifier. Not really relevant here, but this allows unnamed function parameter packs, as in template<class T> void f(T...);
So the new-expression form using a type-id doesn't allow for a variable size, which your example function needs. And the new-expression form using a new-type-id doesn't allow any function parameter lists at all in its syntax tree, so any function type must be introduced in the type-specifier-seq. I think the possibilities are a type-name, a decltype-specifier, or a typename-specifier.
So I guess the decltype-specifier option does give us another stupidly verbose way of writing the expression, but this time without needing a named type alias or an #include
.
auto p6 = new decltype(static_cast<void(*)()>(nullptr))[n];