Vlad from Moscow
Vlad from Moscow

Reputation: 311078

An error is issued by gcc relative to parsing type-id in a new expression

This program

#include <cstddef>

int main()
{
    const std::size_t N1 = 2;
    const std::size_t N2 = 3;

    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
}

does not compile using the compiler C++ gcc HEAD 10.0.0 20190.

The compiler issues error

prog.cc: In lambda function:
prog.cc:8:40: error: expected '{' before ')' token
    8 |    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
      |                                        ^
prog.cc: In function 'int main()':
prog.cc:8:34: error: no match for 'operator*' (operand type is 'main()::<lambda()>')
    8 |    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
prog.cc:8:47: error: expected type-specifier before ';' token
    8 |    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
      |                                               ^

However the program compiles using clang HEAD 10.0.0.

Is the pointer type-id specification ambiguous or is it indeed a gcc bug?

EDIT: By the way if to remove the outer parentheses like

int ( **p )[N1] = new int ( *[N2] )[N1];

then clang also issues an error referencing a lambda

prog.cc:8:38: error: expected body of lambda expression
   int ( **p )[N1] = new int ( *[N2] )[N1];
                                     ^

Upvotes: 3

Views: 191

Answers (2)

Barry
Barry

Reputation: 303517

This may not be the answer you're looking for, but the real answer is:

using T = int(*)[N1];
T *p = new T[N2];

The declarator-based syntax that C and C++ use is practically impossible to understand once you have multiple declarators. They're hard to write... where do you put the parentheses? What's the order? And they're hard to read... can't go left-to-right, can't go right-to-left, have to spiral inside-out.

Just use aliases. Easy to write, easy to read, easy to parse.

Upvotes: 1

L. F.
L. F.

Reputation: 20619

As far as I can tell, this is definitely a bug in the newest versions of GCC. Presumably, GCC is attempting to parse the ( int ( *[N2] )[N1] ) part as a new-placement, i.e., a parenthesized expression list. Now int ( is interpreted as a function-style cast, etc.

Per new-expression:

new-expression:
    ::opt new new-placementopt new-type-id new-initializeropt
    ::opt new new-placementopt ( type-id ) new-initializeropt

There is no special disambiguation rule regarding new-placement vs ( type-id ), so the thing should be interpreted as a type-id if it cannot be interpreted as an expression-list.


The EDIT part is a syntax error per [expr.new]/4:

Note: Parentheses in a new-type-id of a new-expression can have surprising effects. [ Example:

new int(*[10])();               // error

is ill-formed because the binding is

(new int) (*[10])();            // error

Instead, the explicitly parenthesized version of the new operator can be used to create objects of compound types:

new (int (*[10])());

allocates an array of 10 pointers to functions (taking no argument and returning int). — end example ] — end note ]

Upvotes: 1

Related Questions