When trying to get to grips with std::ostream_iterator I have come up with the following code which doesn't compile (under gcc 5.3 or clang 3.6).
#include <iostream>
#include <iterator>
namespace temp {
struct Point {
int x;
};
}
//namespace temp {
//namespace std {
std::ostream& operator<<(std::ostream& s, temp::Point p) {
return s << p.x;
}
//}
int main(int argc, char** argv) {
temp::Point p{1};
std::ostream_iterator{std::cout} = p;
//std::cout << p;
std::cout << std::endl;
return 0;
}
When operator<< is in global scope, compilation throws up a host of template instantiation errors.
However, std::cout << p works fine. And, if operator<< is declare in namespace temp or namespace std, the code compiles and runs as one would expect.
My question is why does a global operator<< not work?
The behavior that you observe is a peculiarity of Two-Phase Lookup process, which is used when resolving names referred from template definitions, and its interaction with Argument Dependent Lookup (ADL).
In your case you use operator = from std::ostream_iterator. Names referred from the definition of std::ostream_iterator::operator = will be looked up through two-phase lookup: non-dependent names are looked up at the first phase (from the definition of operator =), while dependent names are looked up from the point of instantiation (your call to operator =).
Internally, std::ostream_iterator::operator = uses operator << for the given (stream, value) pair. And since the type of value is dependent on template parameter, this reference to operator << is treated as a dependent one. Thus, its resolution is postponed to the second phase.
It is true that the second phase of lookup (performed from the point of instantiation) generally sees more names than the first phase. And you apparently expected your definition of operator << in global namespace to become visible as well.
However, it is crucial to note one important detail about the second phase: at the second phase only the associated namespaces (namespaces brought in by ADL) are "enriched" with additional names visible at the point of instantiation. But the "regular" namespaces (not related to ADL) are not affected by the second phase at all. In the latter namespaces the compiler is still restricted to seeing the same names that were visible at the first phase and nothing else.
That is exactly what the following passage in the standard says
14.6.4 Dependent name resolution [temp.dep.res]
1 In resolving dependent names, names from the following sources are considered:
— Declarations that are visible at the point of definition of the template.
— Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.
This explains what happens in your case. Even though you added an extra operator << to the global namespace, the global namespace is not one of the ADL associated namespaces in this case (only std and temp are). For this reason, the second phase cannot really see your extra << definition.
But if you add your definition to one of ADL associated namespaces, the second phase will immediately notice that addition. This is why your code compiles fine if you define your operator in std or temp namespaces.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With