1

I want to dynamically allocate an array of function pointers. Here are some of my attempts:

#include <type_traits>

void f(int n) {
    // auto p1 = new void(*[n])(); // syntax error

    auto p2 = new (void(*[n])()); // warning, see below

    auto p3 = new typeof(void(*)())[n]; // non-standard extension

    using fptr = void(*)(); auto p4 = new fptr[n]; // verbose, but okay

    auto p5 = new std::add_pointer_t<void()>[n]; // hackish
}

I wonder why p2 results in a warning, given that I cannot write it without parentheses (like in p1)?

(gcc) warning: non-constant array new length must be specified without parentheses around the type-id [-Wvla]

(clang) warning: when type is in parentheses, array cannot have dynamic size

This is a purely theoretical question. I am not looking for an advice how to write good code solving the problem.

Community
  • 1
  • 1
Vladimir Reshetnikov
  • 11,750
  • 4
  • 30
  • 51

1 Answers1

4

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];
Community
  • 1
  • 1
aschepler
  • 70,891
  • 9
  • 107
  • 161