Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Argument-dependent lookup behaving unexpectedly on types aliased from another namespace

I just ran into some interesting behavior with argument-dependent lookup, which I do not fully understand:

#include <iostream>

namespace a {
struct Foo {
  Foo(int v1, int v2) : v1(v1), v2(v2) { }
  int v1,v2;
};
}

namespace b {
template <typename T>
struct Baz : T {
  using T::T;
};
}

namespace c {
using Foo = ::b::Baz< ::a::Foo>;

// (1) NOT FOUND BY ADL
// std::ostream& operator << (std::ostream& os, const Foo& foo)
// {
//   return os << foo.v1 << "," << foo.v2;
// }
}

namespace b {

// (2) FOUND BY ADL
std::ostream& operator << (std::ostream& os, const ::c::Foo& foo)
{
  return os << foo.v1 << "," << foo.v2;
}

}

int main()
{
  c::Foo foo(1,2);
  // Variant (1): ADL fails: should it not find
  //   c::operator<<(std::ostream&, const Foo&) ?
  // Variant (2) compiles
  std::cout << "foo: " << foo << std::endl;
}

I get that c::Foo is actually b::Baz<...>, so it somewhat makes sense that ADL finds the operator when I define it inside namespace b. But it seems to defy intuition that defining the operator inside namespace c does not work, since c::Foo should (IMHO) allow the compiler to perform ADL inside namespace c as well.

Why is that not the case? What is the rationale behind this?

like image 385
mindriot Avatar asked May 04 '16 11:05

mindriot


1 Answers

[basic.lookup.argdep]/2:

Typedef names and using-declarations used to specify the types do not contribute to this set.

Neither typedef names nor using-declarations are affiliated with the type they designate. If they were (which would actually be counterintuitive, IMO), things would break very easily; just because I typedef'd some class, all calls are now considering functions added in the vicinity of the typedef, which is practically never desired:

#include <string>

namespace A {
    using str = std::string;
    int stoi(str); // This will be a candidate in e.g. stoi(std::string{"0.4"}),
                   // leading to potentially different behavior or ambiguity

namespace B {
    int stoi(std::string); // This is no candidate, because...?
}
like image 177
Columbo Avatar answered Oct 16 '22 07:10

Columbo