A question about name lookup with friend function
Asked Answered
M

1

6

I have read the standard section of [basic.lookup.unqual] and I am confused about this:

typedef int f;
namespace N {
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the typedef, not the friend function: equivalent to int(a)
    }
  };
}

Please consider the above code; I don't understand why name f is type int, instead of void f(A &). In my understanding, the name lookup should find void f(A &) firstly in class scope A. If no name can be found there, it would perform a lookup in outside namespace. Obviously, there is a name void f(A &) in class A and as the standard says:

name lookup ends as soon as a declaration is found for the name

So why does the name refer to type int here, if there are other particular rules about these?

Melly answered 23/4, 2020 at 3:39 Comment(3)
@bolov I look forward to reading your very descriptive answer.Acidosis
@JerryJeremiah name lookup does not concern with the type of a initializer in an expression,that's other rules,the lookup only guarante to find out these declarationsMelly
Dup of C++ name lookup - example from the standardCenter
B
7

Firstly, the friend declaration itself doesn't make f visible for name lookup, f could only be found by ADL.

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided

From the standard, [namespace.memdef]/3,

The friend declaration does not by itself make the name visible to unqualified lookup or qualified lookup. [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). — end note ] If a friend function or function template is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments ([basic.lookup.argdep]). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

The problem is for ADL to be applied, whether f(a) is a function call must be determined in advance.

[basic.lookup.unqual]/3,

(emphasis mine)

The lookup for an unqualified name used as the postfix-expression of a function call is described in [basic.lookup.argdep]. [ Note: For purposes of determining (during parsing) whether an expression is a postfix-expression for a function call, the usual name lookup rules apply.

In this stage, the function name f is invisible and the typename f is found, then f(a) is considered not to be a function all, then ADL won't be applied at all.

Because the expression is not a function call, the argument-dependent name lookup ([basic.lookup.argdep]) does not apply and the friend function f is not found.

BTW: Adding the declaration of f at namespace scope makes the function name f visible and f(a) would be considered as function call (and then you'll get the error that f returns void which can't be used to initialize i). e.g.

typedef int f;
namespace N {
  struct A;
  void f(A &);
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the friend function now
    }
  };
}
Breechloader answered 23/4, 2020 at 6:47 Comment(6)
Thanks,a wonderful answers.Because "The friend declaration does not by itself make the name visible to unqualified lookup or qualified lookup" and "For purposes of determining ,the usual name lookup rules apply"(that refers to unqualified lookup),so,"unqualified lookup" cannot find the friend function,only typedef be found,so there's no ADL continue to perform,Is my understanding right?Melly
@jackX Yes, that's the essence of my answer. ;)Breechloader
@Breechloader "...becomes a member of the innermost enclosing namespace of X" The namespace is applied to global namespace too?Abridgment
@Abridgment If the innermost enclosing namespace is the global namespace, yes.Breechloader
@Breechloader Thanks! I have a related question about ADL; ...namespaces and classes associated with the types of the function arguments Could you make this part more clearer? I mean, the class is the argument type's class and the namespace is where the class is defined? EDIT) IMHO, It seems that (2.1)~(2.7) are applied to the namespaces and classes. It is little complicated :)Abridgment
@Abridgment the class is the argument type's class and the namespace is where the class is defined? Yes. E.g. given namespace my { struct X {}; void foo(X) {} }, you can call as my::X x; foo(x); directly, no need to write it as my::foo(x);.Breechloader

© 2022 - 2024 — McMap. All rights reserved.