Title
Ambiguity in friend declaration syntax
Status
cd1
Section
_N4567_.5.1.1 [expr.prim.general]
Submitter
Martin von Loewis

Created on 1999-06-07.00:00:00 last changed 189 months ago

Messages

Date: 2004-03-15.00:00:00

[Voted into WP at March 2004 meeting.]

Date: 2003-04-15.00:00:00

Notes from April 2003 meeting:

See also issue 96.

The proposed change resolves only part of issue 215.

Date: 2003-04-15.00:00:00

Proposed resolution (April 2003):

nested-name-specifier is currently defined in _N4567_.5.1.1 [expr.prim.general] paragraph 7 as:

    nested-name-specifier:
      class-or-namespace-name :: nested-name-specifieropt
      class-or-namespace-name :: template nested-name-specifier
    class-or-namespace-name:
      class-name
      namespace-name

The proposed definition is instead:

    nested-name-specifier:
      type-name ::
      namespace-name ::
      nested-name-specifier identifier ::
      nested-name-specifier templateopt template-id ::

Issue 215 is addressed by using type-name instead of class-name in the first alternative. Issue 125 (this issue) is addressed by using identifier instead of anything more specific in the third alternative. Using left association instead of right association helps eliminate the need for class-or-namespace-name (or type-or-namespace-name, as suggested for issue 215).

It should be noted that this formulation also rules out the possibility of A::template B::, i.e. using the template keyword without any template arguments. I think this is according to the purpose of the template keyword, and that the former rule allowed such a construct only because of the difficulty of formulation of a right-associative rule that would disallow it. But I wanted to be sure to point out this implication.

Date: 2002-04-15.00:00:00

Notes from the 4/02 meeting:

The changes look good. Clark Nelson will merge the two proposals to produce a single proposed resolution.

Date: 2001-10-15.00:00:00

Notes from 10/01 meeting:

It was pointed out that the proposed resolution does not deal with cases like X::Y where X is a type but not a class type. The working group reaffirmed its decision that the disambiguation should be syntactic only, i.e., it should depend only on whether or not the name is a type.

Jason Merrill :

At the Seattle meeting, I suggested that a solution might be to change the class-or-namespace-name in the nested-name-specifier rule to just be "identifier"; there was some resistance to this idea. FWIW, I've tried this in g++. I had to revise the idea so that only the second and subsequent names were open to being any identifier, but that seems to work just fine.

So, instead of

    nested-name-specifier:
      class-or-namespace-name :: nested-name-specifieropt
      class-or-namespace-name :: template nested-name-specifier

it would be

    nested-name-specifier:
      type-or-namespace-name :: (per issue 215)
      nested-name-specifier identifier ::
      nested-name-specifier template template-id ::

Or some equivalent but right-associative formulation, if people feel that's important, but it seems irrelevant to me.

Clark Nelson :

Personally, I prefer the left-associative rule. I think it makes it easier to understand. I was thinking about this production a lot at the meeting, considering also some issues related to 301. My formulation was getting kind of ugly, but with a left-associative rule, it gets a lot nicer.

Your proposal isn't complete, however, as it doesn't allow template arguments without an explicit template keyword. You probably want to add an alternative for:

    nested-name-specifier type-or-namespace-name ::

There is admittedly overlap between this alternative and

    nested-name-specifier identifier ::

but I think they're both necessary.

Date: 2001-04-15.00:00:00

Proposed resolution (04/01):

(See below for problem with this, from 10/01 meeting.)

In _N4567_.5.1.1 [expr.prim.general] paragraph 7,

  1. Before the grammar for qualified-id, start a new paragraph 7a with the text

    A qualified-id is an id-expression that contains the scope resolution operator ::.
  2. Following the grammar fragment, insert the following:

    The longest sequence of tokens that could form a qualified-id constitutes a single qualified-id. [Example:

        // classes C, D; functions F, G, namespace N; non-class type T
        friend C ::D::F();   // ill-formed, means friend (C::D::F)();
        friend C (::D::F)(); // well-formed
        friend N::T ::G();   // ill-formed, means friend (N::T::G)();
        friend N::T (::G)(); // well-formed
    

    end example]

  3. Start a new paragraph 7b following the example.

(This resolution depends on that of issue 215.)

Date: 2017-02-06.00:00:00

The example below is ambiguous.

    struct A{
      struct B{};
    };

    A::B C();

    namespace B{
      A C();
    }

    struct Test {
      friend A::B ::C();
    };
Here, it is not clear whether the friend declaration denotes A B::C() or A::B C(), yet the standard does not resolve this ambiguity.

The ambiguity arises since both the simple-type-specifier (9.2.9.3 [dcl.type.simple] paragra 1) and an init-declararator (9.3 [dcl.decl] paragraph 1) contain an optional :: and an optional nested-name-specifier (_N4567_.5.1.1 [expr.prim.general] paragraph 1) . Therefore, two different ways to analyse this code are possible:

simple-type-specifier = A::B
init-declarator = ::C()
simple-declaration = friend A::B ::C();
or
simple-type-specifier = A
init-declarator = ::B::C()
simple-declaration = friend A ::B::C();
Since it is a friend declaration, the init-declarator may be qualified, and start with a global scope.

Suggested Resolution: In the definition of nested-name-specifier, add a sentence saying that a :: token immediately following a nested-name-specifier is always considered as part of the nested-name-specifier. Under this interpretation, the example is ill-formed, and should be corrected as either

    friend A (::B::C)();   //or
    friend A::B (::C)();

An alternate suggestion — changing 9.2 [dcl.spec] to say that

The longest sequence of tokens that could possibly be a type name is taken as the decl-specifier-seq of a declaration.

— is undesirable because it would make the example well-formed rather than requiring the user to disambiguate the declaration explicitly.

History
Date User Action Args
2008-10-05 00:00:00adminsetstatus: wp -> cd1
2004-04-09 00:00:00adminsetmessages: + msg1005
2004-04-09 00:00:00adminsetstatus: ready -> wp
2003-11-15 00:00:00adminsetstatus: review -> ready
2003-04-25 00:00:00adminsetmessages: + msg819
2003-04-25 00:00:00adminsetmessages: + msg818
2003-04-25 00:00:00adminsetstatus: drafting -> review
2002-05-10 00:00:00adminsetmessages: + msg638
2001-11-09 00:00:00adminsetmessages: + msg566
2001-11-09 00:00:00adminsetstatus: review -> drafting
2000-11-18 00:00:00adminsetmessages: + msg409
2000-11-18 00:00:00adminsetstatus: drafting -> review
2000-02-23 00:00:00adminsetstatus: open -> drafting
1999-06-07 00:00:00admincreate