By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
429,435 Members | 2,033 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 429,435 IT Pros & Developers. It's quick & easy.

Why koenig lookup?

P: n/a
The so-called koenig lookup allows doing odd things like this:

#include <algorithm>
#include <string>

int main()
{
std::string table[10];
sort(table, table+10);
}

(Notice how the 'sort' doesn't have the std:: prefix, yet it's
perfectly valid.)

I understand that the reason for the koenig lookup to exist is so that
you can write things like:

std::cout << 5;

This is actually equivalent to:

operator<<(std::cout, 5);

The koenig lookup allows the compiler to look for operator<< inside
the std namespace because one of the parameters is from that namespace.

With operators it makes sense. However, why perform the same lookup
for *all* functions? What's the advantage? Couldn't it be simply defined
that the koenig lookup is performed for operators but not for regular
functions?
Aug 9 '08 #1
Share this Question
Share on Google+
16 Replies


P: n/a
Juha Nieminen wrote:
The so-called koenig lookup allows doing odd things like this:

#include <algorithm>
#include <string>

int main()
{
std::string table[10];
sort(table, table+10);
}

(Notice how the 'sort' doesn't have the std:: prefix, yet it's
perfectly valid.)

I understand that the reason for the koenig lookup to exist is so that
you can write things like:

std::cout << 5;

This is actually equivalent to:

operator<<(std::cout, 5);

The koenig lookup allows the compiler to look for operator<< inside
the std namespace because one of the parameters is from that namespace.

With operators it makes sense. However, why perform the same lookup
for *all* functions? What's the advantage? Couldn't it be simply defined
that the koenig lookup is performed for operators but not for regular
functions?
Well it saves a lot of typing, i think that's why it's working for
functions, too. If the function is valid in the namespace std, why
should you type it all the time. If you don't want to use std just don't
type "using namespace std;" at the beginning of your code. The compiler
will demand std:: all the time then.

If you type the line the compiler doesn't need the std:: anymore,
because you are already in the namespace std.
Aug 9 '08 #2

P: n/a
Captain Trips wrote:
Well it saves a lot of typing, i think that's why it's working for
functions, too. If the function is valid in the namespace std, why
should you type it all the time. If you don't want to use std just don't
type "using namespace std;" at the beginning of your code. The compiler
will demand std:: all the time then.

If you type the line the compiler doesn't need the std:: anymore,
because you are already in the namespace std.
Uh? I think you are talking about a completely different issue. I was
not talking about "using" at all.
Aug 9 '08 #3

P: n/a
In article <g7**********@sagnix.uni-muenster.de>, Captain Trips
<us*******@uni-muenster.dewrote:
[Koenig lookup]

Well it saves a lot of typing, i think that's why it's working for
functions, too. If the function is valid in the namespace std, why
should you type it all the time. If you don't want to use std just don't
type "using namespace std;" at the beginning of your code. The compiler
will demand std:: all the time then.
That was his point; it does NOT need std:: here, even though there is no
"sort" in the scope of main, and no using directive bringing namespace std
into scope. Simply because table, one of the arguments to the function
call, was of a type whose definition was in namespace std, the sort
function was chosen.
Aug 9 '08 #4

P: n/a
On Aug 9, 5:08 pm, Juha Nieminen <nos...@thanks.invalidwrote:
The so-called koenig lookup allows doing odd things like this:
#include <algorithm>
#include <string>
int main()
{
std::string table[10];
sort(table, table+10);
}
(Notice how the 'sort' doesn't have the std:: prefix, yet it's
perfectly valid.)
I understand that the reason for the koenig lookup to exist is
so that you can write things like:
std::cout << 5;
This is actually equivalent to:
operator<<(std::cout, 5);
The koenig lookup allows the compiler to look for operator<<
inside the std namespace because one of the parameters is from
that namespace.
With operators it makes sense. However, why perform the same
lookup for *all* functions? What's the advantage? Couldn't it
be simply defined that the koenig lookup is performed for
operators but not for regular functions?
What's the difference between a function and an operator, except
the invocation syntax? Orthogonality, if nothing else, requires
that the same lookup applies. In addition:

#include <BigDecimal.hh>

int
main()
{
using SomeNamespace::BigDecimal ;
BigDecimal z1 ;
BigDecimal z2 = sin( z1 ) ;
// Do you really think it reasonable to have to write
// SomeNamespace::sin here?
}

And of course, in a template, you don't really even have the
choice.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 10 '08 #5

P: n/a
On Aug 9, 7:46 pm, "Alf P. Steinbach" <al...@start.nowrote:
* Juha Nieminen:
The so-called koenig lookup allows doing odd things like
this:
#include <algorithm>
#include <string>
int main()
{
std::string table[10];
sort(table, table+10);
}
(Notice how the 'sort' doesn't have the std:: prefix, yet
it's perfectly valid.)
I understand that the reason for the koenig lookup to exist
is so that you can write things like:
std::cout << 5;
This is actually equivalent to:
operator<<(std::cout, 5);
The koenig lookup allows the compiler to look for operator<<
inside the std namespace because one of the parameters is
from that namespace.
With operators it makes sense. However, why perform the same
lookup for *all* functions? What's the advantage? Couldn't
it be simply defined that the koenig lookup is performed for
operators but not for regular functions?

I don't think Andrew Koenig, or /anyone else/, fully grokked
the consequences at the time.
Well, he certainly didn't have any concrete experience with it
before it was standardized. However...
If ADL (argument-dependent lookup) was defined today, now that
we've had some experience with it, then I'm pretty sure it
would be restricted to operators, and perhaps -- but just
perhaps -- extended in another direction, namely for lookup
of arguments in classes, where it's a hassle to have to
qualify things all the time, as opposed to the case
exemplified above, where it just introduces problems and
complexity without conferring any advantage.
A few experimental implementations had been made with namespace,
and I think it was concrete experience with them that led to the
understanding that something had to be done with functions. All
functions, not just overloaded operators. Now, given the time
frame, I personally think that the "correct" solution would have
been to back out of the problem completely, and drop namespaces
from that version of the standard, because of a lack of
knowledge with regards to the real problems that it might
introduce. (And in honesty, I have to admit that my fears were
largely unfounded.)

Off hand, I think if there were anything to do differently
(given namespaces and the way they work), it would be to have
the built in types imply the global namespace, rather than no
namespace.
I guess that goes to show that the job of standardization is
not to innovate. ;-)
I think at least some members of the committee share your
opinion there---at least part of the motivation behind Boost was
to provide a place where innovation would be appropriate, which
could be standardized once concrete experience was established.
(Regretfully, it only applies to libraries.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 10 '08 #6

P: n/a
On Aug 10, 3:19 pm, "Alf P. Steinbach" <al...@start.nowrote:
* James Kanze:
[...]
In addition:
#include <BigDecimal.hh>
int
main()
{
using SomeNamespace::BigDecimal ;
BigDecimal z1 ;
BigDecimal z2 = sin( z1 ) ;
// Do you really think it reasonable to have to write
// SomeNamespace::sin here?
}
I think this is a good point, that an overload of 'sin' should
not be introduced in a new namespace, but in the original
namespace, namely here the global one.
It doesn't work, because...
And of course, in a template, you don't really even have the
choice.
?
In a template, sin will be a dependent name, and will not be
looked up in the global namespace, only in the namespace where
BigDecimal was defined.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 10 '08 #7

P: n/a
On Aug 10, 6:45 pm, "Alf P. Steinbach" <al...@start.nowrote:
* James Kanze:
>And of course, in a template, you don't really even have the
choice.
?
In a template, sin will be a dependent name, and will not be
looked up in the global namespace, only in the namespace where
BigDecimal was defined.
If that was true then it would just reinforce the point that
ADL for named routines has turned out to be too problematic,
too many limiting corner cases.
I don't think the problem here is ADL. The problem is two phase
lookup in general.
However,
<code>
#include <iostream>
namespace james
{
struct BigInt {};
}
double sin( james::BigInt ) { return 0; }
template< typename T >
void foo()
{
sin( james::BigInt() );
}
is good standard C++.
But doesn't use sin in a dependent context in a dependent
context in the template. I was thinking of the classical case,
where the operator<< for std::vector<intdefined in global
namespace doesn't get found when it is used in
std::ostream_iterator. Curiously, I can't reproduce the
symptomes in a simple example, but I've definitely had the
problem myself, and people post it here from time to time.

I did say dependent, didn't I, in my first posting. Now, either
two phase lookup is too complicated for even a guru like
yourself to catch onto the subtilities, or you're being
intentionally obtuse. (And regretfully, there's nothing ironic
about that. IMHO, two phase lookup does have enough subtilities
that even an expert can easily overlook them.)
So what you literally write, is incorrect, and argument void.
But still there is a good chance that you had some specific
scenario in mind, that I don't at present grok. And I'm
interested in that, on its own, so please, elaborate. :-)
See above. And the fact that even someone with your level
didn't catch on immediately just confirms my feeling that two
phase lookup, at least in its present form, was a mistake,
because it's just too complicated.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 10 '08 #8

P: n/a
In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
@k13g2000hse.googlegroups.com>, ja*********@gmail.com says...

[ ... ]
But doesn't use sin in a dependent context in a dependent
context in the template. I was thinking of the classical case,
where the operator<< for std::vector<intdefined in global
namespace doesn't get found when it is used in
std::ostream_iterator. Curiously, I can't reproduce the
symptomes in a simple example, but I've definitely had the
problem myself, and people post it here from time to time.
Here's a fairly simple example:

#include <map>
#include <string>
#include <iostream>
#include <algorithm>

typedef std::map<std::string, ints_int;

//namespace std {

std::ostream &operator<<(std::ostream &os, s_int::value_type const &v)
{
return os << v.first << ": " << v.second;
}

//}

int main() {
s_int m;

m["a"] = 123;
m["b"] = 345;

std::copy(m.begin(), m.end(),
std::ostream_iterator<s_int::value_type>(std::cout , "\n"));

return 0;
}

As it stands right now, it won't compile -- but remove the two comment
delimiters so operator<< is in namespace std, and it compiles and works
just fine.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Aug 10 '08 #9

P: n/a
Jerry Coffin wrote:
In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
@k13g2000hse.googlegroups.com>, ja*********@gmail.com says...

[ ... ]
>But doesn't use sin in a dependent context in a dependent
context in the template. I was thinking of the classical case,
where the operator<< for std::vector<intdefined in global
namespace doesn't get found when it is used in
std::ostream_iterator. Curiously, I can't reproduce the
symptomes in a simple example, but I've definitely had the
problem myself, and people post it here from time to time.

Here's a fairly simple example:

#include <map>
#include <string>
#include <iostream>
#include <algorithm>

typedef std::map<std::string, ints_int;

//namespace std {

std::ostream &operator<<(std::ostream &os, s_int::value_type const &v)
{
return os << v.first << ": " << v.second;
}

//}

int main() {
s_int m;

m["a"] = 123;
m["b"] = 345;

std::copy(m.begin(), m.end(),
std::ostream_iterator<s_int::value_type>(std::cout , "\n"));

return 0;
}

As it stands right now, it won't compile -- but remove the two comment
delimiters so operator<< is in namespace std, and it compiles and works
just fine.
Lemme guess. operator<<(std::ostream&, int ) is ***NOT*** in global
namespace Right? .. Which is the whole point!

I'm with Alf Steinbach on this one....

regards
Andy Little
Aug 11 '08 #10

P: n/a
kwikius wrote:
Jerry Coffin wrote:
>In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
@k13g2000hse.googlegroups.com>, ja*********@gmail.com says...

[ ... ]
>>But doesn't use sin in a dependent context in a dependent
context in the template. I was thinking of the classical case,
where the operator<< for std::vector<intdefined in global
namespace doesn't get found when it is used in
std::ostream_iterator. Curiously, I can't reproduce the
symptomes in a simple example, but I've definitely had the
problem myself, and people post it here from time to time.

Here's a fairly simple example:

#include <map>
#include <string>
#include <iostream>
#include <algorithm>

typedef std::map<std::string, ints_int;

//namespace std {
std::ostream &operator<<(std::ostream &os, s_int::value_type const &v)
{ return os << v.first << ": " << v.second;
}

//}

int main() { s_int m;

m["a"] = 123;
m["b"] = 345;

std::copy(m.begin(), m.end(),
std::ostream_iterator<s_int::value_type>(std::cou t, "\n"));

return 0;
}

As it stands right now, it won't compile -- but remove the two comment
delimiters so operator<< is in namespace std, and it compiles and
works just fine.

Lemme guess. operator<<(std::ostream&, int ) is ***NOT*** in global
namespace Right? .. Which is the whole point!

I'm with Alf Steinbach on this one....

regards
Andy Little
Oh I see..

hmm C++ is too complicated. Bring back BASIC I say :-(

regards
Andy Little
Aug 11 '08 #11

P: n/a
Alf P. Steinbach wrote:
* kwikius:
>kwikius wrote:
>>Jerry Coffin wrote:
In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
@k13g2000hse.googlegroups.com>, ja*********@gmail.com says...

[ ... ]

But doesn't use sin in a dependent context in a dependent
context in the template. I was thinking of the classical case,
where the operator<< for std::vector<intdefined in global
namespace doesn't get found when it is used in
std::ostream_iterator. Curiously, I can't reproduce the
symptomes in a simple example, but I've definitely had the
problem myself, and people post it here from time to time.

Here's a fairly simple example:

#include <map>
#include <string>
#include <iostream>
#include <algorithm>

typedef std::map<std::string, ints_int;

//namespace std {
std::ostream &operator<<(std::ostream &os, s_int::value_type const &v)
{ return os << v.first << ": " << v.second;
}

//}

int main() { s_int m;

m["a"] = 123;
m["b"] = 345;

std::copy(m.begin(), m.end(),
std::ostream_iterator<s_int::value_type>(std::c out, "\n"));

return 0;
}

As it stands right now, it won't compile -- but remove the two
comment delimiters so operator<< is in namespace std, and it
compiles and works just fine.
Lemme guess. operator<<(std::ostream&, int ) is ***NOT*** in global
namespace Right? .. Which is the whole point!

I'm with Alf Steinbach on this one....

Oh I see..

hmm C++ is too complicated. Bring back BASIC I say :-(

Well, the rule's simple: if you place an overload in the original
namespace, it will most likely be considered by original code there,
otherwise it may not.

It's like this:

#include <math.h>

namespace james
{
struct BigInt {};
double sin( BigInt ) { return 0; }
void foo() { sin( 3.14 ); } // *\/o
}

int main()
{
james::foo();
}

Here the sinful overload of sin placed in namespace james causes
compilation failure of foo. So I guess you're still with me on this...
Yes? <g>
Cheers,

- Alf
Well IIRC if you use VC8 then <cmathincludes <math.hand then

using ::sin; etc

which is another situation again.

Anyway rather than discussing what does happen and why, which AFAICS is
extremely complicated (I can't find a way to get Jerry Coffins example
to find the function in global to create an ambiguity, which AFAICS
means global namespace is not considered in that case... whatever that
case actually is), I'm more interested in figuring out not even how to
fix it in C++, but if there is a simple solution to connecting a set of
UDT's to their correct expression generically(in my fictional
programming language 'Bob')

...and meanwhile in the real world I've had to bite the bullet and start
getting to grips with Latex... no not that sort. the Knuth, Tex latex..

http://miktex.org/

.... cool !

but I'm with you in spirit...

regards
Andy Little

Aug 11 '08 #12

P: n/a
On Aug 11, 3:37 am, "Alf P. Steinbach" <al...@start.nowrote:
* kwikius:
[...]
Well, the rule's simple: if you place an overload in the
original namespace, it will most likely be considered by
original code there, otherwise it may not.
Are you sure of that?
It's like this:

#include <math.h>

namespace james
{
struct BigInt {};
double sin( BigInt ) { return 0; }
void foo() { sin( 3.14 ); } // *\/o
}
int main()
{
james::foo();
}
Here the sinful overload of sin placed in namespace james
causes compilation failure of foo. So I guess you're still
with me on this... Yes? <g>
Yes. Good example of why 1) build-in types should be associated
with global namespace (rather than no namespaces) and 2)
functions which manipulate built-in types should be in global
namespace:-). (Your example doesn't involve ADL, but name
hiding. And it would work *IF* the argument of sin triggered
ADL. The problem here is that ADL doesn't apply to built in
types.)

Note that I'm not really arguing that ADL is a good thing per
se, but rather that it is not the real problem. Rather, the
fact that we need it is a symptom of a problem elsewhere; if we
just dropped it, without changing the way namespaces worked,
we'd be in worse shape than we are now.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 11 '08 #13

P: n/a
The koenig lookup allows the compiler to look for operator<< inside
the std namespace because one of the parameters is from that namespace.

With operators it makes sense. However, why perform the same lookup
for *all* functions? What's the advantage? Couldn't it be simply defined
that the koenig lookup is performed for operators but not for regular
functions?
Here is a good (but quite long) article which explain this :
http://www.gotw.ca/publications/mill02.htm

--
Sebastien
Aug 11 '08 #14

P: n/a
On Aug 11, 8:04*am, "Alf P. Steinbach" <al...@start.nowrote:
* Alf P. Steinbach:

I now think I/we? have been blaming the wrong guy, ADL.

The whole thing about global namespace operator<< overload problem, beingjust a
case of name hiding.
what is the best solution for the operator<< example presented above?
I runned into the same problem once and I putted my operator<< inside
std:: as a quick non-std solution :-(

Diego
HP
Aug 11 '08 #15

P: n/a
On Aug 11, 8:10 pm, Diego Martins <jose.di...@gmail.comwrote:
On Aug 11, 8:04 am, "Alf P. Steinbach" <al...@start.nowrote:
* Alf P. Steinbach:
I now think I/we? have been blaming the wrong guy, ADL.
The whole thing about global namespace operator<< overload
problem, being just a case of name hiding.
what is the best solution for the operator<< example presented
above? I runned into the same problem once and I putted my
operator<< inside std:: as a quick non-std solution :-(
You mean overloading operator<< for something like
std::vector<double>? The best solution is not to do it, at all.
What happens if a collegue is also using std::vector<double>,
and also wants to overload<< for it?

What you want is a type with known semantics, or rather, with a
known standard display format. In mathematics, for example,
you'd almost certainly have a Vector class, with various
additional operators not supported by std::vector<double>.
Unless it was a Polynome class. In exceptional cases where you
really do have a raw std::vector, you can create a small
decorator class which does the trick:

template< typename T >
class AsVector // mathematical vector...
{
public:
explicit AsVector( std::vector< T const& v )
: myV( v )
{
}
friend std::ostream&
operator<<( std::ostream& dest,
AsVector const& source ) ;

private:
std::vector< T const&
myV ;
} ;

template< typename T >
inline AsVector< T >
asVector( std::vector< T const& v )
{
return AsVector< T >( v ) ;
}

AsPolynome would be similar, except that the code for the
operator<< would be very different.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Aug 12 '08 #16

P: n/a
"Alf P. Steinbach" <al***@start.nowrote in message
news:Cc******************************@posted.comne t...
I don't think Andrew Koenig, or /anyone else/, fully grokked the
consequences at the time.
Well, that kind of depends.

First, let me emphasize that I did not invent the idea of argument-dependent
lookup, nor was I particularly fond of the idea. The main reason my name
got hung on it was that I played a significant role in convincing the
standards committee that something like it was necessary.

The examples that show the need to find operators are too well known for me
to need to elaborate them further. The examples that are the real killers,
however, come up when we start using templates. Consider:

template <class Tvoid foo(T t1, T t2) {
// ...
T t3 = t1 + t2;
T t4 = combine(t1, t2);
// ...
};

I hope you will agree with me that it would not be a good thing if there
were no way to decide where to look up the + in t1 + t2. But if you agree
there, then what can we say about looking up "combine" in combine(t1, t2)?
Shouldn't it be possible to write

namespace Mine {
class Thing { /* ... */ };
Thing operator+(Thing, Thing);
Thing combine(Thing, Thing);
// ...
}

and then call foo(Mine::Thing(), Mine::Thing())?

If it's necessary for foo to be able to find Mine::operator+ by looking at
the type of t1 and t2, isn't it equally necessary for foo to be able to find
Mine::combine for the same reason? Notice that in both cases, there is *no*
way for the author of foo to specify a namespace explicitly, because the
author of foo has no way of knowing what namespace to specify.

This problem first came to my attention pretty late in the standardization
process, and I realized that if the standard as it then stood were to be
approved, neither t3 nor t4's initializing expression would be able to be
compiled. So I explained the problem to the committee, mentioned that if we
didn't do anything about it, we would be faced with a lot of pretty obvious
programs that would not compile, and it was pretty much a matter of going
with the best we could come up with in the time available or revising the
schedule.

I don't recall having an opinion at the time about which was the better
course of action. However, it is now more than ten years later, and I don't
seem to see a consensus on a better solution to the problem--which suggests
that perhaps delaying the standard wouldn't have made things any better.

So I guess it comes down to which consequences I grokked. What I did know
is that we had discovered a very serious problem, that we did not know a
perfect solution, and that we had to do *something*. Short of reworking all
the name lookup rules from scratch, it's far from clear to me what other
alternatives we had.
Aug 25 '08 #17

This discussion thread is closed

Replies have been disabled for this discussion.