Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't a using directive affect ADL?

I am trying to understand why the following code does not compile:

namespace ns {
  struct S {};
}

namespace alleq {
  inline bool
  operator==(const ns::S &, const ns::S &)
  {
    return true;
  }
}

namespace ns {
  using namespace alleq;
  // using alleq::operator==; // Works if you uncomment this
}

ns::S a;

void
f()
{
  ::ns::operator==(a, a); // OK
  a == a;                 // Error: no match for 'operator=='
}

The first line of function f does compile, which makes me believe that namespace ns contains a function operator==. However, when I compare two values of type ns::S, this operator== function is not found. By contrast, a using declaration does work as expected, and allow the second line of f to find ns::operator== by ADL.

I suspect the reason has to do with the fact that a using directive is supposed to make the symbol appear as if it is in the global namespace :: (because that is the common ancestor of namespaces alleq and ns). But if that were really the case, then why would ::ns::operator== find the function?

More generally, I'm trying to provide some useful operator== (and related) overloads in a library, but not force people to use the definitions if they don't want them. I was hoping to allow people to enable or not enable operator== (and related other operators) on their types based on whether they import a dedicated operator namespace into their own namespace. Now it looks like people may have to write a host of using declarations (which I could simplify with a macro, but yuck).

like image 333
user3188445 Avatar asked Dec 18 '14 10:12

user3188445


1 Answers

I suspect the reason has to do with the fact that a using directive is supposed to make the symbol appear as if it is in the global namespace :: (because that is the common ancestor of namespaces alleq and ns).

That's sort of true, but only for unqualified lookup, not argument-dependent lookup:

7.3.4 [namespace.udir]:

  1. A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup (3.4.1), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.

Since that only applies to unqualified name lookup, it only really helps when performing name lookup from within the enclosing namespace, when name lookup looks "outwards" into enclosing namespaces. It doesn't help find unqualified names from outside the namespace (which is what ADL does).

And the next paragraph states:

  1. A using-directive does not add any members to the declarative region in which it appears.

i.e. A using directive makes names visible in a scope, but does not add new declarations to the scope.

The reason for the behaviour you see is simply that the standard says ADL ignores using directives:

3.4.2 [basic.lookup.argdep]

  1. When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2) except that:
    — Any using-directives in the associated namespace are ignored.
like image 167
Jonathan Wakely Avatar answered Oct 18 '22 14:10

Jonathan Wakely