This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 121a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2026-10-10


3160. Grammar ambiguity with class member declarations

Section: 11.4  [class.mem]     Status: tentatively ready     Submitter: Tam S. B.     Date: 2025-08-08

(From submission #741.)

Consider:

  typedef int t1, t2, t3, t4;

  struct A {
    static A(*t1)(); // #1, function pointer
    static A(t2)();  // #2, invalid constructor declaration
    static A(t3);    // #3, invalid constructor declaration
    A(t4);           // #4, constructor
  };

A disambiguation rule is missing that would cause the indicated (desired) outcomes.

Suggested resolution [SUPERSEDED]:

Add a new subclause at the end of 11.4 [class.mem]:

Ambiguity resolution [class.mem.ambig]

An ambiguity can arise in a member-declaration when a type-name is enclosed in parentheses. In this case, the choice is between the declaration of a function with an unnamed parameter and the declaration of a member with redundant parentheses around the declarator-id. The resolution is to consider the type-name as a simple-type-specifier rather than a declarator-id. As in 8.11 [stmt.ambig], the disambiguation is purely syntactic and can result in an ill-formed interpretation.

  typedef int t1, t2, t3;
  template<class> struct u1;
  struct A {
    A(t1);            // OK, declares constructor having unnamed parameter of type int
    typedef A(t2);    // error: constructor declaration with typedef
    friend A(t3)();   // error: declares function named A that returns a function with no return type
    A(u1);            // error: placeholder for deduced class type cannot appear in this context (9.2.9.8 [dcl.type.class.deduct])
  };

CWG 2026-04-28

The disambiguation rule should probably be limited to constructors (which would use an injected-class-name). See also issue 3163.

Proposed resolution (approved by CWG 2026-10-06):

Add a new subclause at the end of 11.4 [class.mem]:

Ambiguity resolution [class.mem.ambig]

An ambiguity can arise in a member-declaration when a type-name is enclosed in parentheses. In this case, the choice is between the declaration of a function with an unnamed parameter and the declaration of a member with redundant parentheses around the declarator-id. If the former interpretation would declare a function whose name is the injected-class-name of the enclosing class or class template and its decl-specifier-seq, if present, would contain neither friend nor a decl-specifier that is not permitted for a constructor (11.4.5.1 [class.ctor.general]), the former interpretation is chosen (i.e. the type-name is considered a simple-type-specifier rather than a declarator-id); otherwise, the latter interpretation is chosen. As in 8.11 [stmt.ambig], the disambiguation is purely syntactic and can result in an ill-formed interpretation. [Example:
  typedef int t1, t2;
  template<class> struct u1;
  struct A {
   A(t1);         // OK, declares constructor having unnamed parameter of type `int`, not a data member named `t1`
   typedef A(t2); // OK, declares `t2` as an alias for `A`
   A(u1);         // error: placeholder for deduced class type cannot appear in this context (9.2.9.8 [dcl.type.class.deduct])
  };
—end example]