Используя директиву - почему так странное поведение?

У меня есть этот пример кода

namespace ns1
{
    void foo(int)
    {
    }
}

namespace ns2
{
    void foo()
    {
    }
    void bar()
    {
        using namespace ::ns1;
        foo(42); // why compiler can't just call ns1::foo?
    }
}

И он не компилируется с ошибкой:

prog.cpp:16:9: error: too many arguments to function ‘void ns2::foo()’

Я нашел причину этой ошибки в стандарте С++ 2003:

Директива using указывает, что имена в номинированном пространстве имен может использоваться в области, в которой появляется директива use после директива use. При поиске неквалифицированного имени (3.4.1), имена отображаются так, как если бы они были объявлены в ближайшем охватывающем пространстве имен который содержит как директиву, так и назначенное пространство имен.[Примечание: в этом контексте "содержит" означает "содержит непосредственно или косвенно".]

Является ли любое обоснование для этого странного правила? Почему имена из пространства имен ns1 не могут непосредственно отображаться в пространстве имен ns2?

Ответ 1

Я считаю, что это было/сделано в попытке уменьшить конфликты и сюрпризы. Имена, введенные в область видимости с помощью директивы using, видны, но все, что непосредственно содержится в локальной области, будет иметь приоритет над ней.

Если вы действительно хотите вызвать функцию без квалифицированного идентификатора, вы делаете ее видимой с помощью объявления using:

namespace ns1 {
    void foo(int) { }
}

namespace ns2 {
    void foo() { }
    void bar() {
        using ::ns1::foo;
        foo(42); // No problem; calls ::ns1::foo.
    }
}

Ответ 2

ns1::foo скрывается декларацией ns2::foo

Из N3337, §3.3.10/1 [basic.scope.hiding]

Имя может быть скрыто явным объявлением того же имени во вложенной декларативной области или производном классе (10.2).

В разделе, которое вы указали (§7.3.4/2), сразу следует

3 Директива using не добавляет никаких членов в декларативный регион, в котором он появляется. [Пример:

    namespace A {
      int i;
      namespace B {
        namespace C {
          int i;
        }
        using namespace A::B::C;
        void f1() {
          i = 5; // OK, C::i visible in B and hides A::i
        }
      }
      // more (irrelevant) stuff
    }

-end пример]

В вашем случае директива using вводит имена в ns1 в пространство имен общих предков, где оно появляется, и имя ns1, что означает глобальное пространство имен. Он не вводит их в пределах ns2, поэтому объявление ns2::foo скрывает значение ns1::foo.

Если вы хотите найти ns1::foo, используйте вместо этого использование объявления.

void bar()
{
    using ::ns1::foo;
    foo(42);
}