CWG Issue 138 (original) (raw)

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

2025-04-13


138. Friend declaration name lookup

Section: _N4868_.9.8.2.3 [namespace.memdef]Status: CD6Submitter: Martin von LoewisDate: 14 Jul 1999

[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]

_N4868_.9.8.2.3 [namespace.memdef] paragraph 3 says,

If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace... When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.

It is not clear from this passage how to determine whether an entity is "first declared" in a friend declaration. One question is whether a using-declaration influences this determination. For instance:

void foo();
namespace A{
  using ::foo;
  class X{
friend void foo();
  };
}

Is the friend declaration a reference to ::foo or a different foo?

Part of the question involves determining the meaning of the word "synonym" in 9.10 [namespace.udecl] paragraph 1:

A using-declaration introduces a name into the declarative region in which the using-declaration appears. That name is a synonym for the name of some entity declared elsewhere.

Is "using ::foo;" the declaration of a function or not?

More generally, the question is how to describe the lookup of the name in a friend declaration.

John Spicer: When a declaration specifies an unqualified name, that name is declared, not looked up. There is a mechanism in which that declaration is linked to a prior declaration, but that mechanism is not, in my opinion, via normal name lookup. So, the friend always declares a member of the nearest namespace scope regardless of how that name may or may not already be declared there.

Mike Miller: 6.5.3 [basic.lookup.unqual] paragraph 7 says:

A name used in the definition of a class X outside of a member function body or nested class definition shall be declared in one of the following ways:... [Note: when looking for a prior declaration of a class or function introduced by a frienddeclaration, scopes outside of the innermost enclosing namespace scope are not considered.]

The presence of this note certainly implies that this paragraph describes the lookup of names in friend declarations.

John Spicer: It most certainly does not. If that section described the friend lookup it would yield the incorrect results for the friend declarations of f and g below. I don't know why that note is there, but it can't be taken to mean that that is how the friend lookup is done.

void f(){}
void g(){}
class B {
    void g();
};
class A : public B {
    void f();
    friend void f(); // ::f not A::f
    friend void g(); // ::g not B::g
};

Mike Miller: If so, the lookups for friend functions and classes behave differently. Consider the example in 6.5.6 [basic.lookup.elab] paragraph 3:

struct Base {
    struct Data;         // _OK: declares nested_ Data
    friend class Data;   // _OK: nested_ Data _is a friend_
};

If the friend declaration is not a reference to::foo, there is a related but separate question: does thefriend declaration introduce a conflicting (albeit "invisible") declaration into namespace A, or is it simply a reference to an as-yet undeclared (and, in this instance, undeclarable)A::foo? Another part of the example in 6.5.6 [basic.lookup.elab] paragraph 3 is related:

struct Data {
    friend struct Glob;  // _OK: Refers to (as yet) undeclared_ Glob
                         // _at global scope._
};

John Spicer: You can't refer to something that has not yet been declared. The friend is a declaration of Glob, it just happens to declare it in a such a way that its name cannot be used until it is redeclared.

(A somewhat similar question has been raised in connection withissue 36. Consider:

namespace N {
    struct S { };
}
using N::S;
struct S;          // legal?

According to 11.3 [class.name] paragraph 2,

A declaration consisting solely of _class-key identifier ;_is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name.

Should the elaborated type declaration in this example be considered a redeclaration of N::S or an invalid forward declaration of a different class?)

(See also issues95,136,139,143,165, and166, as well as paper J16/00-0006 = WG21 N1229.)