» Sign in
  

Daveed Vandevoorde

Koenig lookup is also called argument-dependent name lookup (ADL). Koenig lookup applies to the lookup of unqualified function names used for function calls.

For example, it applies to a call of the form:

 f(x, y, z); // unqualified

but not to a call of the form:

 N::f(x, y, z); // qualified

Ordinary name lookup (OL) looks up qualified names in the nearest enclosing scope where the name is used, and if not found, the lookup proceeds in successively enclosing scope until the name is found. OL terminates as soon as the name is found (even if the name is not appropriate for the given use; for example, if f above turned out to be an integer variable, OL would not attempt to find a better f).

If indeed we are in a function call situation, ADL kicks in on top of OL. ADL looks at the types of the arguments and may associate some additional name spaces and classes with these types. For example, if the type is a class type, a pointer to a class type, or some other derivative of a class type, the name space in which that class was declared is associated with that type. The class type itself is also associated with that type.

All the call arguments are examined this way, and the associated name spaces and classes are collected. The name of the function is then looked up in all the name spaces and classes; if found, the corresponding function declarations are added to the overload set.

Here's a bizarre example:

 namespace N {
    struct X;
    void f(X*, int); // (1)
 }

 void g() {
    void f(X*, long); // (2)
    X *p;
    f(p, 0);
 }

OL finds declaration (2); if ADL did not exist, that would be it. However, with ADL, the compiler must now perform overload resolution between (1) and (2); and (1) is preferred because "0" has type int, not long.

There is a connection to friend function declarations. To explain this, let me first give the example:

 struct S {
    friend void h1(); // (3)
    friend void h2(S&); // (4)
 };

 struct R {
    operator S&();
 };

 void bar() {
    S s;
    h1(); // Error!
    h2(s); // OK.
    R r;
    h2(r); // Error!
 }

In the old days, declaring friend functions like (3) and (4) made those declarations immediately visible in the surrounding name space (possibly global) scope. In those days, all the uses in bar would have been fine. This is called friend name injection, because the name of the friend is "injected" in another scope. It is a particularly interesting "feature" when combined with template instantiation, because the injection occurs at whatever turns out to be the point of instantiation.

In ISO/ANSI C++, however, the name does not become visible in the surrounding scope. If that was all there was to it, all of the calls in bar would be illegal. However, there is one reliance on friend name injection that was deemed reasonable by the C++ committee: inline friend definitions of operators applying to the class.

So the C++ committee decided to introduce a mechanism to make such operators work, and found that ADL could be used to do this. Basically, the rule is that friend declarations are visible when found through ADL because an argument of the call happens to be associated with a class of which the function is a friend. This explains why the call h2(s)in bar works: s has class type S, so during ADL we look up h2 in S and find the h2 friend declaration.

Note that it is the arguments (as opposed to the parameters) that determine ADL. So the call h2(r) doesn't work, because the class type associated with r is R - the implicit user-defined conversion is not considered during ADL.

What about HP aC++? By default, current versions of HP aC++ will not perform ADL, but will stick to the old behavior of making friend declarations visible in the surrounding name space scope. However, if you add the option -Wc,-koenig_lookup,on, the ADL is enabled and friend name injection is inhibited. To compile the example above, add some declarations in the name space scope:

 struct S {
    friend void h1();
    friend void h2(S&);
 };

 void h1();    // Inject names
 void h2(S&);  // manually

 struct R {
    operator S&();
 };

 void bar() {
    S s;
    h1(); // OK.
    h2(s); // OK.
    R r;
    h2(r); // OK.
 }



Manage My AllianceOne Membership

 
 » Sign in
 » Join AllianceOne
 » Contact us