I'll change it slightly so the fall-back declaration of bar
isn't a template (= shorter code), and don't use SFINAE as this is purely a name lookup issue.
namespace foo {
int bar(int);
}
namespace feature_test {
namespace detail_overload {
void bar(...);
}
namespace detail {
using namespace detail_overload;
using namespace foo;
void test() { bar(0); } // (A)
}
}
In line (A), the compiler needs to find the name bar
. How is it looked up? It's not argument-dependent, so it must be unqualified lookup: [basic.lookup.unqual]/2
The declarations from the namespace nominated by a using-directive become visible in a namespace enclosing the using-directive; see 7.3.4. For the purpose of the unqualified name lookup rules described in 3.4.1, the declarations from the namespace nominated by the using-directive are considered members of that enclosing namespace.
Note they become in an enclosing namespace, not the enclosing namespace. The details from [namespace.udir]/2 reveal the issue:
[...] 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.
That is, for the name lookup of bar
inside test
:
namespace foo {
int bar(int);
}
// as if
using foo::bar;
namespace feature_test {
namespace detail_overload {
void bar(...);
}
// as if
using detail_overload::bar;
namespace detail {
// resolved
// using namespace detail_overload;
// using namespace foo;
void test() { bar(0); } // (A)
}
}
Therefore, the name bar
found in feature_test
hides the name (not) found in the global scope.
Note: Maybe you can hack around this issue with argument-dependent name lookup (and a second SFINAE). If something comes to my mind, I'll add it.