Is the following snippet correct C++ code?
#include <sstream>
class Foo;
std::ostream& operator<<(std::ostream& str, Foo x); // (A)
namespace test {
class Message {
public:
std::ostringstream str;
};
template<typename T>
Message& operator<<(Message& m, T& t)
{
using ::operator<<;
m.str << t;
return m;
}
}
namespace detail {
class Class {
public:
int i;
Class() : i(5) {}
};
}
std::ostream& operator<<(std::ostream& str, detail::Class& myClass) { // (B)
return str << myClass.i;
}
int main() {
test::Message m;
detail::Class c;
m << c;
}
According to http://goo.gl/NkPNau GCC compiles this fine, while Clang doesn't find the operator<<
(B).
In case you are wondering: This is from a code that uses GTest with a custom operator<<
for std::set
to print nice assertion messages. We couldn't figure out a way to make it work with clang other than putting the operator<<
(B) in std namespace (yeah I know...).
Clang is correct here. Let's call g++'s behavior a language extension.
Argument-dependent lookup (aka Koenig lookup) does apply, since m.str << t
is interpreted using the best overload matching either m.str.operator<<(t)
or operator<<(m.str, t)
, and the second case is an unqualified-id as the function name. But:
14.6.4.2:
For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that:
For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.
If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.
At the template definition context, (B) is not visible. (B) is visible at the template instantiation context, but the global namespace is not an associated namespace of either std::ostringstream
or detail::Class
.
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