Friday, March 21, 2008

Koenig lookup in C++

This is a nerdcentral post, so non-nerds (all two of you) can stop reading now.

I was dealing with some piece of code at work which looked like this:


// In some header file
namespace N {
class C {
...
};

void DoStuff(const C& c_obj) { ... }
} // namespace N

// In some .cc file
N::C c_obj;
...
DoStuff(c); // <-- Look! No need for N::DoStuff


I looked at DoStuff(c) without the N:: prefix and figured that it must be a bug. Someone forgot to put it in. But this code compiles fine - what gives?

I remember reading about this way back when I was really into C++ and thought it was the hottest stuff on the planet. Thankfully those days are behind me, but not everything that I learnt. So I dug into this a bit and finally came across Herb Sutter's article explaining exactly this.

Before you read the article or even read this one furthur, its a good exercise to try and figure out what's going on. What are the possibilities that would make this work? A good guess is someone did a
using namespace N;
somewhere up above. This would indeed make things work and this is why using namespace declarations are a very, very bad idea in header files. They're usually a bad idea in .cc files too, but less so than headers.

However, the above example isn't because of a using namespace somewhere up above. Instead, it is because of a feature of C++ called Koenig lookup. The basic idea is that besides the usual places where a compiler looks to resolve a symbol (local scope, global scope, etc) it must also look in the namespaces containing each of the parameters of the function. As a result, our good ol' compiler looks within namespace N as well, and lo and behold, there's DoStuff defined in N.

Why is this useful?
STL uses this feature quite a bit. Its "normal" to declare things like this:


namespace stl {
template
class datastructure {...}
}
} // namespace stl

template
void operator+(stl::datastructure& s1, const stl::datastructure& s2)


I'm not certain why this is better than declaring operator+ to be a part of datastructure itself (it will have the same effect), but this is C++ arcane voodoo that is beyond me. Anyway, I prefer using better tools these days, but now and then, we have to deal with languages that were really not intended for the ordinary mortal programmer.

1 comment:

Ashwin said...

Interesting indeed! Did NOT know this despite going knee-deep in C++ every once in a while.

Regarding why operator+ is not within the class; that's because you want the compiler to perform implicit conversions on _both_ the arguments (e.g., if you don't do this, you will not be able to support expression "2 + datastructure")