This concept is well explained in the Wikipedia, and also in the codeproject. What confuses me is how this magic mentioned in Wikipedia is played:

While ADL makes it practical for free functions to be part of the interface of a class, it makes namespaces less strict and so can require the use of fully-qualified names when they would not otherwise be needed. For example, the C++ standard library makes extensive use of unqualified calls to std::swap to swap two values.

Here is my lousy approach to mimic the magic:

#include <iostream>
using namespace std;

void g();

void g() { cout << "g is outside NS" << endl; }

namespace NS {
        class A{};
        void f(A) { g(); cout << "f is called" << endl; }
        void g() { cout << "g is inside NS" << endl; }
}

int main()
{
        NS::A a;
        f(a);
}

with the following output:

g is outside NS
f is called

It looks good, suppose customized is missing, aka the Ln 6 is deleted. Compile, link, oops, gcc complains that the undefined reference to `g()’. Let’s move the g in the namespace before the f:

namespace NS {
        class A{};
        void g() { cout << "g is inside NS" << endl; }
        void f(A) { g(); cout << "f is called" << endl; }
}

It works, and the g inside NS namespace is called, no doubt, there is only g defined here. Let’s add a global g and check whether customized function is called, the full source code is:

#include <iostream>
using namespace std;

void g();

void g() { cout << "g is outside NS" << endl; }

namespace NS {
        class A{};
        void g() { cout << "g is inside NS" << endl; }
        void f(A) { g(); cout << "f is called" << endl; }
}

int main()
{
        NS::A a;
        f(a);
}

Wow, the output is:

g is inside NS
f is called

The global g is never called by NS::f! Is there something wrong? Please leave a feedback if you have an answer.

Share and Enjoy:
  • Print this article!
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks

2 Comments to “C++ Learning Note(5): Koenig Lookup”

  1. Moreshwar | October 27th, 2007 at 8:36 pm

    No, I think everything is correct in this case.
    Consider what happens here :

    void foo()
    {
    int bar = 0;
    {
    int bar = 10;
    cout << bar << endl;
    }
    }

    I think this should print 10 and not 0. This is simple scope resolution and hiding principles right?

  2. Eden Crane | October 31st, 2008 at 2:46 pm

    to mimic the magic, along with your function ‘f’ INSIDE of the NS, you need a function ‘f’ OUTSIDE of NS.

    Forget about the ‘g’ function for a minute and take a look at the following code:

    #include
    using namespace std;

    namespace NS {
    class A{};
    void f(A) { cout << “f INSIDE called” << endl; }
    }

    template
    void f(T) { cout << “f OUTSIDE is called” << endl; }

    int main()
    {
    NS::A a;
    f(a);
    }

    What is the expected output of this code? We have 2 functions named ‘f’, and in ‘main’ we are only using the namespace ‘std’. A beginning C++ programmer would expect the OUTSIDE version of ‘f’ to be called.

    Run the program, output is:
    f INSIDE is called

    When we call f(A), because the A class resides in the NS namespace, the NS namespace is checked for a function f(A) where a match is found. When we called f(A) we did not specify the NS namespace at all, but thanks to Koenig lookup, the NS namespace version was used.

    With regards to the Wikipedia comment, here is the concern of that writer. Let’s say you want to write a special version of std::swap for your class, to make the operation more efficient.

    #include
    using namespace std;

    namespace NS {
    class A{};
    void f(A) { cout << “f INSIDE called” << endl; }
    void swap(A& one,A& two) { cout << “swap INSIDE called”;}
    }

    template
    void f(T) { cout << “f OUTSIDE is called” << endl; }

    int main()
    {
    NS::A a;
    f(a);

    NS::A b;
    swap(a,b);
    }

    Output is:
    f INSIDE called
    cout INSIDE called

    Thanks to Koenig lookup, your version of ‘swap’ inside of the NS namespace will be called, even though the program is using the std namespace. The output of this program is:

    The problem is, some people like to use fully-qualified names. If we take the above code, remove the ‘using namespace std’ and replace all calls to the std namespace, we get:

    #include

    namespace NS {
    class A{};
    void f(A) { std::cout << “f INSIDE called” << std::endl; }
    void swap(A& one,A& two) { std::cout << “swap INSIDE called” << std::endl;}
    }

    template
    void f(T) { std::cout << “f OUTSIDE is called” << std::endl; }

    int main()
    {
    NS::A a;
    f(a);

    NS::A b;
    std::swap(a,b);
    }

    Output is:
    f INSIDE called

    In this case, the fears of the wikipedia writer are realized. We have one programmer who designed the NS namespace and thought that he could slip in an alternate version of the std::swap function call. It works if users of the NS namespace use /unqualified/ calls to swap, but it fails to work if the users of the NS namespace use /fully-qualified/ calls to std::swap.

Leave a Comment