| ||
|
This community works best when people use their real names. Please
register for a free account.
Other Groups: Joel on Software Business of Software Design of Software (CLOSED) .NET Questions (CLOSED) TechInterview.org CityDesk FogBugz Fog Creek Copilot The Old Forum Your hosts: Albert D. Kallal Li-Fan Chen Stephen Jones |
I noticed that std::pair doesn't have an insertion or extraction operator. Easily fixed, I'll just write one: #include <iostream> #include <utility> template<typename charT, typename traits, typename T1, typename T2> inline std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits> &in, std::pair<T1, T2> &p) { return in >> p.first >> p.second; } template<typename charT, typename traits, typename T1, typename T2> inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits> &out, const std::pair<T1, T2> &p) { return out << p.first << " " << p.second; } And then I'll use them: #include <iostream> #include <utility> void main(void) { std::pair<int, int> pairOfInt(1, 2); std::cout << pairOfInt << std::endl; std::ostream_iterator< std::pair<int, int> > oi(std::cout, "\n"); std::cin >> pairOfInt; std::istream_iterator< std::pair<int, int> > ii(std::cin); } Other than the complaints about unused variables, this seems perfectly innocuous. Except that the last declaration, of the istream_iterator<>, blows up -- rather messily in gcc version 3.2 (at least) and VC++ 7.1 (at least). The solution is to pollute the std:: namespace by declaring operator>> and operator<< in std::. Technically, this is wrong because users aren't supposed to put anything into that namespace. What is the simple thing that I'm missing? (The simple thing is not a missing header file or a different, easy-to-fix compile error. It's something about name lookup between namespaces (I think) that I don't understand.)
Hmmm, I don't think that there is anything too simple to explain here.. The namespace-lookup mechanism is called "Koenig lookup", if you want to dig up some more information on it. It basically means that when the compiler tries to look for a match to a function call, it will try to automatically fill in any undecorated names by also looking in the namespaces of any arguments to the function. But after thinking about it a bit, I don't think that particular mechanism has anything to do with your particular issue. What seems to happen here is that the use of operator << and >> before the istream_iterator work because the code in main is in the global namespace, and you have provided operators for << and >> in the global namespace. The istream_iterator doesn't work because the way istream_iterator is written, it ends up calling operator >> in some of its internal code, on some member variables of the istream_iterator class, where both the function call and all the variables involved are all contained within namespace std:: . Then you run into one of the rules for overloaded functions - if there are some overloads of a function within the namespace (as there are in this case, since there are the other provided stream operators for ints and stuff like that), then the compiler won't automatically jump out to match other functions with the same name in the global namespace (where your provided code is living). That's why you have to sometimes add that global scoping operator like ::MessageBox(), etc... I don't remember the exact reasoning behind why the overloading works that way - some kind of concern about pulling in code unexpectedly or something. It's probably covered in The Design and Evolution of C++ somewhere under overloading. But I think that's the rule that you're running into here, it's just kind of hard to see it since it is happening to code hidden inside istream_iterator. I'd say go ahead and jam your stuff inside namespace std. You might be able to dig up some other info by doing a google groups (note groups, not web) search on: "namespace overloading group:comp.lang.c++.moderated"
Michael Wednesday, August 24, 2005
Michael, that was a well-thought out and intelligent post. The thing that really confuses me is the the ostream_iterator doesn't crash and burn, only the istream_iterator. I'll keep looking in Herb Sutter's book Exceptional C++ and see if I can find it. The Evolution book sounds like a good one also, I haven't checked that one out yet. Anyone else?
> The thing that really confuses me is the the > ostream_iterator doesn't crash and burn, only > the istream_iterator. That's because you're not actually using the iterator to do anything. If you try to use the ostream_iterator (for instance, put a "*ii = pairOfInt;" in after you declare the ostream_iterator), you should see it fail in the same way. The difference is that the istream_iterator ends up calling operator >> right away when it is created, and caches the value that it read. ostream_iterator doesn't use operator << until the iterator is actually used. You'll only see the error when you have created a code path that uses those operators.
Michael Thursday, August 25, 2005
To further endorse Michael's suggestion of The Design and Evolution of C++ (or D&E as many of us call it). Here's a recent post by Bjarne Stroustrup in comp.lang.c++.moderated in response to book suggestions on the topic of C++ history and decisions. <begin blockquote> "I think these two books should be considered separately. The ARM is seriously pre-standard and I have not recommended it for close to a decade now. D&E on the other hand is the (still) best explanation of the fundamental design desisions for C++ and IMO invaluable for anyone who want to say "C++ does it this way because ...". Many silly conjectures in that direction could be avoided by a quick look in D&E. Apart from that, I consider history important for understanding the world, and D&E also contains quite a few historical facts." -- Bjarne </end>
Jared Thursday, August 25, 2005 | |
Powered by FogBugz
