Why koenig lookup?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Juha Nieminen

    Why koenig lookup?

    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?
  • Captain Trips

    #2
    Re: Why koenig lookup?

    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.

    Comment

    • Juha Nieminen

      #3
      Re: Why koenig lookup?

      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.

      Comment

      • blargg

        #4
        Re: Why koenig lookup?

        In article <g7keh7$urf$1@s agnix.uni-muenster.de>, Captain Trips
        <usenet.25@un i-muenster.dewrot e:
        [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.

        Comment

        • James Kanze

          #5
          Re: Why koenig lookup?

          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.h h>

          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:james.kan ze@gmail.com
          Conseils en informatique orientée objet/
          Beratung in objektorientier ter Datenverarbeitu ng
          9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

          Comment

          • James Kanze

            #6
            Re: Why koenig lookup?

            On Aug 9, 7:46 pm, "Alf P. Steinbach" <al...@start.no wrote:
            * 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:james.kan ze@gmail.com
            Conseils en informatique orientée objet/
            Beratung in objektorientier ter Datenverarbeitu ng
            9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

            Comment

            • James Kanze

              #7
              Re: Why koenig lookup?

              On Aug 10, 3:19 pm, "Alf P. Steinbach" <al...@start.no wrote:
              * James Kanze:
              [...]
              In addition:
              #include <BigDecimal.h h>
              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:james.kan ze@gmail.com
              Conseils en informatique orientée objet/
              Beratung in objektorientier ter Datenverarbeitu ng
              9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

              Comment

              • James Kanze

                #8
                Re: Why koenig lookup?

                On Aug 10, 6:45 pm, "Alf P. Steinbach" <al...@start.no wrote:
                * 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<int defined in global
                namespace doesn't get found when it is used in
                std::ostream_it erator. 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:james.kan ze@gmail.com
                Conseils en informatique orientée objet/
                Beratung in objektorientier ter Datenverarbeitu ng
                9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                Comment

                • Jerry Coffin

                  #9
                  Re: Why koenig lookup?

                  In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
                  @k13g2000hse.go oglegroups.com> , james.kanze@gma il.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<int defined in global
                  namespace doesn't get found when it is used in
                  std::ostream_it erator. 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::s tring, ints_int;

                  //namespace std {

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

                  //}

                  int main() {
                  s_int m;

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

                  std::copy(m.beg in(), m.end(),
                  std::ostream_it erator<s_int::v alue_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.

                  Comment

                  • kwikius

                    #10
                    Re: Why koenig lookup?

                    Jerry Coffin wrote:
                    In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
                    @k13g2000hse.go oglegroups.com> , james.kanze@gma il.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<int defined in global
                    >namespace doesn't get found when it is used in
                    >std::ostream_i terator. 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::s tring, ints_int;
                    >
                    //namespace std {
                    >
                    std::ostream &operator<<(std ::ostream &os, s_int::value_ty pe const &v)
                    {
                    return os << v.first << ": " << v.second;
                    }
                    >
                    //}
                    >
                    int main() {
                    s_int m;
                    >
                    m["a"] = 123;
                    m["b"] = 345;
                    >
                    std::copy(m.beg in(), m.end(),
                    std::ostream_it erator<s_int::v alue_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

                    Comment

                    • kwikius

                      #11
                      Re: Why koenig lookup?

                      kwikius wrote:
                      Jerry Coffin wrote:
                      >In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
                      >@k13g2000hse.g ooglegroups.com >, james.kanze@gma il.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<int defined 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::s tring, ints_int;
                      >>
                      >//namespace std {
                      >std::ostream &operator<<(std ::ostream &os, s_int::value_ty pe const &v)
                      >{ return os << v.first << ": " << v.second;
                      >}
                      >>
                      >//}
                      >>
                      >int main() { s_int m;
                      >>
                      > m["a"] = 123;
                      > m["b"] = 345;
                      >>
                      > std::copy(m.beg in(), m.end(),
                      >std::ostream_i terator<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
                      Oh I see..

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

                      regards
                      Andy Little

                      Comment

                      • kwikius

                        #12
                        Re: Why koenig lookup?

                        Alf P. Steinbach wrote:
                        * kwikius:
                        >kwikius wrote:
                        >>Jerry Coffin wrote:
                        >>>In article <2a0f00c3-10cd-4b64-8203-57c5e681b565
                        >>>@k13g2000hse .googlegroups.c om>, james.kanze@gma il.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<int defined in global
                        >>>>namespace doesn't get found when it is used in
                        >>>>std::ostrea m_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::s tring, ints_int;
                        >>>>
                        >>>//namespace std {
                        >>>std::ostre am &operator<<(std ::ostream &os, s_int::value_ty pe const &v)
                        >>>{ return os << v.first << ": " << v.second;
                        >>>}
                        >>>>
                        >>>//}
                        >>>>
                        >>>int main() { s_int m;
                        >>>>
                        >>> m["a"] = 123;
                        >>> m["b"] = 345;
                        >>>>
                        >>> std::copy(m.beg in(), m.end(),
                        >>>std::ostream _iterator<s_int ::value_type>(s td::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....
                        >>
                        >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 <cmathinclude s <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..



                        .... cool !

                        but I'm with you in spirit...

                        regards
                        Andy Little

                        Comment

                        • James Kanze

                          #13
                          Re: Why koenig lookup?

                          On Aug 11, 3:37 am, "Alf P. Steinbach" <al...@start.no wrote:
                          * 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:james.kan ze@gmail.com
                          Conseils en informatique orientée objet/
                          Beratung in objektorientier ter Datenverarbeitu ng
                          9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                          Comment

                          • =?ISO-8859-1?B?U+liYXN0aWVuIE0u?=

                            #14
                            Re: Why koenig lookup?

                            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 :


                            --
                            Sebastien

                            Comment

                            • Diego Martins

                              #15
                              Re: Why koenig lookup?

                              On Aug 11, 8:04 am, "Alf P. Steinbach" <al...@start.no wrote:
                              * 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

                              Comment

                              Working...