Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using directive -- why so strange behaviour?

Tags:

c++

I have this sample code

namespace ns1
{
    void foo(int)
    {
    }
}

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

And it doesn't compile with error:

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

I found reason of this error in C++ 2003 standard:

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. [Note: in this context, “contains” means “contains directly or indirectly”. ]

Is any rationale for this strange rule? Why names from namespace ns1 can't directly appear in namespace ns2?

like image 413
maxim.yurchuk Avatar asked Dec 11 '14 22:12

maxim.yurchuk


Video Answer


2 Answers

I believe this was/is done in an attempt at reducing conflicts and surprises. Names brought into scope by a using directive are visible, but anything that's directly contained in the local scope will take precedence over it.

If you really want to call you function without a qualified ID, you make it visible with a using declaration instead:

namespace ns1 {
    void foo(int) { }
}

namespace ns2 {
    void foo() { }
    void bar() {
        using ::ns1::foo;
        foo(42); // No problem; calls ::ns1::foo.
    }
}
like image 75
Jerry Coffin Avatar answered Nov 10 '22 08:11

Jerry Coffin


ns1::foo is being hidden by the declaration of ns2::foo

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

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).

The section you've quoted (§7.3.4/2) is immediately followed by

3   A using-directive does not add any members to the declarative region in which it appears. [ Example:

    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 example ]

In your case, the using-directive is introducing the names in ns1 to the common ancestor namespace of where it appears, and that of ns1, meaning the global namespace. It does not introduce them within ns2, so the declaration of ns2::foo hides that of ns1::foo.

If you want ns1::foo to be found, use a using declaration instead.

void bar()
{
    using ::ns1::foo;
    foo(42);
}
like image 39
Praetorian Avatar answered Nov 10 '22 10:11

Praetorian