4

I have the following code:

//mystd plays a role of the std namespace
//which does not allow any new overloads in it
//so we define one in the global namespace
namespace mystd {

template<typename T>
struct A {};

}

//if one uncomment this, everything works as designed
//int f(const mystd::A<int>& v);

//this some namespace which is supposed to be the main one
namespace a {

template<typename T>
int f(const T& v) {
  return 0;
}

template<typename T>
int operator-(const T& v) {
  return 0;
}

}

//this should be different from ::a
//so that `using namespace a;` would import global functions 
//as well as all functions from namespace a, since 
//the global namespace is the nearest common namespace
//see http://www.open-std.org/jtc1/sc22/open/n2356/dcl.html#namespace.udir 
namespace b {

template<typename T>
int Call(const T& v) {
  using namespace ::a;
  return -v + f(v);
}

}

int operator-(const mystd::A<int>&) {
  return 1;
}
int f(const mystd::A<int>&) {
  return 2;
}

int main() {
  //why this returns 1, not 3?
  //what is the difference between operator-() & f()?
  return b::Call(mystd::A<int>());
}

The idea is to imitate the behavior of gtest operator<< & PrintTo, so that one could overload behaviour for some std classes, which can not be done through ADL since adding anything to std namespace is not allowed. However, if I try to do it with a usual function f(), then the overload for f(mystd::A()) is considered to be defined too late (if one comments the template definition of f() the gcc gives note: ‘int f(const mystd::A<int>&)’ declared here, later in the translation unit)

The question is why the behaviour is different for operator-() and f()?

  • 3
    [The return value is 0 if compiled with clang](http://coliru.stacked-crooked.com/a/6b51e48dbada84a3) – cpplearner Jul 12 '15 at 08:47
  • Actually, I discovered that results are different in clang (returns 0), gcc (gives 1) & visual studio (gives 3). Is that an UB? – Dmitry Panteleev Jul 12 '15 at 08:49
  • It's more like bugs in some (or all) of these three compilers. It is quite well-known that VS doesn't implement the correct name lookup rule for names in templates. – cpplearner Jul 12 '15 at 09:05
  • 1
    "`using namespace a;` would import global functions as well as all functions from namespace a" - no, that's not what that quote says. A *using directive* doesn't really import anything; for unqualified name lookup, it makes the names in the designated namespace appear *as if* they were declared in the nearest enclosing namespace [...]; in this case, that namespace is the global namespace. So, within that `return` statement in `Call`, the two templates declared in `a` appear as if they were declared in the global namespace, because the lookup for both involves unqualified lookup. – bogdan Jul 12 '15 at 09:26
  • 3
    Clang is right in my opinion; the result should be `0`. In this case, the lookup for `operator -` should be the same as for `f`. The key rules are in [14.6.4.2] ([temp.dep.candidate]), first bullet point in particular. Sorry, I don't have time for a proper answer at the moment; if nobody else provides one, I'll write one later on. – bogdan Jul 12 '15 at 09:37
  • @bogdan Thanks, looks like you are right with the causes. I was confused by the fact that this (using namespace) was used in gtest and they claim it does work (see gtest/gtest-printers.h). However, when I actually tried to define my own behaviour for outputting std:vector, then it did not work and they used their own printers. – Dmitry Panteleev Jul 12 '15 at 10:04
  • try `using a::f;` and `using a::operator-;` – celtschk Jul 12 '15 at 10:44
  • @celtschk While that changes the underlying mechanism through which those names become visible in `Call`, it doesn't change the outcome in this case - the result should still be `0` in a correct implementation. – bogdan Jul 12 '15 at 16:21
  • @DmitryPanteleev I suppose you're referring to `DefaultPrintNonContainerTo` in `gtest-printers.h`. That will only work correctly if you declare your `operator <<` before you include the `gtest` header, making it visible in the definition context of their template. The fact that it works with later declarations of operator overloads is, as far as I can tell, a bug in GCC (and MSVC, but it's well-known that MSVC doesn't implement dependent name lookup properly). – bogdan Jul 12 '15 at 17:10
  • @DmitryPanteleev As far as your overload for `std::vector` not being picked up, that happened for a different reason: look at the comments in the definition of the `PrintTo` template (line 357 in trunk). The tests for whether the type is a container happen first, before getting to the other mechanism in `DefaultPrintNonContainerTo` (hence the name), because they want their format to be used for standard containers. It looks like you're not supposed to change that. – bogdan Jul 12 '15 at 17:18
  • Anyway, regarding your original question, is everything clear regarding the standard behaviour for that lookup, or could you still use a complete answer? – bogdan Jul 12 '15 at 17:23
  • @bogdan No, everything works like you've explained. And here is the bug in gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51577 The answer is useful only as something I could mark as correct – Dmitry Panteleev Jul 12 '15 at 22:14

0 Answers0