Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Nested `namespace` `using` Name Lookup Order of Preference

I was reading about using-directives on cppreference.com and they had some code I couldn't figure out the order preference for name lookup.

I have read about the the transitive property of using-directives on paragraph 3, unqualified_lookup#namespace scope, and scope#namespace_scope. I also tried searching around on some other sites.

If there is some more documentation you think I should read, please suggest them.

Their code is the following:

Don't spend too much time reading this code because I will be talking about my adapted version below.

namespace A {
    int i;
}
namespace B {
    int i;
    int j;
    namespace C {
        namespace D {
            using namespace A; // all names from A injected into global namespace
            int j;
            int k;
            int a = i; // i is B::i, because A::i is hidden by B::i
        }
        using namespace D; // names from D are injected into C
                           // names from A are injected into global namespace
        int k = 89; // OK to declare name identical to one introduced by a using
        int l = k;  // ambiguous: C::k or D::k
        int m = i;  // ok: B::i hides A::i
        int n = j;  // ok: D::j hides B::j
    }
}

I have adapted their code to print things out:

I put numbered questions as comments on ones I don't understand. You do not have to answer all questions if you can explain the order or name lookup and you think I could answer the rest of the questions myself.

If my questions are too confusing, can you instead try to explain each of the variable name lookups in cppreference's code above?

#include <iostream>
using namespace std;

namespace A {
    int b = 0;
    int i = 1;
}
namespace B {
    int b = 2;
    int i = 3;
    int j = 4;
    namespace C {
        namespace D {
            // 1) Why does cppreference say A is injected into `global`
            //    and not `D` namespace?
            using namespace A; // all names from A injected into global namespace
            int j = 5;
            int k = 6;
            int a = i; // i is B::i, because A::i is hidden by B::i
        }
        using namespace D; // names from D are injected into C
        // 2) Why does cppreference say A is injected into `global` and
        //    not `C` namespace?
                           // names from A are injected into global namespace
        int k = 7; // OK to declare name identical to one introduced by a using
        // 3) What makes this ambiguous and not "one hides the other"?
        // int l = k;  // ambiguous: C::k or D::k
        int m = i;  // ok: B::i hides A::i
        int n = j;  // ok: D::j hides B::j
        int c = b;
    }
}

int main() 
{
    cout << "A::b " << A::b << endl; // prints "A::b 0"
    cout << "A::i " << A::i << endl; // prints "A::i 1"

    cout << endl;

    cout << "B::b " << B::b << endl; // prints "B::b 2"
    cout << "B::i " << B::i << endl; // prints "B::i 3"
    cout << "B::j " << B::j << endl; // prints "B::j 4"

    cout << endl;

    cout << "B::C::a " << B::C::a << endl; // prints "B::C::a 3"
    cout << "B::C::b " << B::C::b << endl; // prints "B::C::b 0"
    cout << "B::C::c " << B::C::c << endl; // prints "B::C::c 2"
    cout << "B::C::i " << B::C::i << endl; // prints "B::C::i 1"
    cout << "B::C::j " << B::C::j << endl; // prints "B::C::j 5"
    cout << "B::C::k " << B::C::k << endl; // prints "B::C::k 7"
    cout << "B::C::m " << B::C::m << endl; // prints "B::C::m 3"
    cout << "B::C::n " << B::C::n << endl; // prints "B::C::n 5"

    cout << endl;

    cout << "B::C::D::a " << B::C::D::a << endl; // prints "B::C::D::a 3"
    cout << "B::C::D::b " << B::C::D::b << endl; // prints "B::C::D::b 0"
    cout << "B::C::D::i " << B::C::D::i << endl; // prints "B::C::D::i 1"
    cout << "B::C::D::j " << B::C::D::j << endl; // prints "B::C::D::j 5"
    cout << "B::C::D::k " << B::C::D::k << endl; // prints "B::C::D::k 6"

    cout << endl;
    return 0;
}

Full Output:

I put numbered questions as comments on ones I don't understand.

I suggest you open the above code side by side so you can see what I'm referencing. I kept lines < 80 chars.

A::b 0
A::i 1

B::b 2
B::i 3
B::j 4

B::C::a 3 // 4) cppreference says A::i == 1 is hidden by B::i == 3
          //    so this is == 3 and not 1.
          //    Why doesn't A::i hide B::i?
          //    Doesn't the `using namespace A` make A::i closer in scope
          //    than B::i?
          //    Why does this go up the parent blocks C->B->B::i == 3 and
          //    not up the namespaces C->D->A->A::i == 1?

B::C::b 0 // 5) This is == A::b == 0. This goes through the
          //    `using namespace D` which contains `using namespace A`.
          //    Why does this go up the namespaces C->D->A->A::b == 0 and
          //    not the parent blocks to C->B->B::b == 2 like in question 4?
B::C::c 2 // 6) This is == B::b == 2 (go up blocks C->B->B::b == 2).
          //    Why is it not == B::C:b == 0
          //    (go up namespaces C->D->A->A::b == 0 like in question 5)
          //    from the assignment `int c = b`?
          //    I'm guessing because name lookup for b
          //    inside the namespace body is different than the B::C::b lookup
          //    outside of the namespace body.
B::C::i 1 // 7) Compared to question 9 below, i is assigned to m but 1 != 3.
          //    I think this is similar to question 6 where name lookup
          //    outside of the namespace body is different than inside.
          //    I'm not sure why this goes up namespaces C->D->A->A::i == 1
          //    and not blocks C->B->B::i == 3.
B::C::j 5 // 8) Why does it go up namespaces C->D->D::j and not blocks
          //    C->B->B::j == 4?
B::C::k
B::C::m 3 // 9) cppreference says B::i hides A::i so this is == B::i == 3
          //    Why does this go up blocks C->B->B::i == 3 and not namespaces
          //    C->D->A->A::i == 1?
          //    Actually, I guess questions 9 and 7 is the same situation as
          //    questions 6 and 5, respectively. Where m and i corresponds
          //    with c and b, respectively.
B::C::n 5 // 10) cppreference says D::j hides B::j so this is == D::j == 5
          //     Why does this go up namespaces C->D->D::j == 5 and not
          //     blocks C->B->B::j == 4? The only difference I see between
          //     question 9 and 10 is that for question 9, i isn't declared
          //     within D like j is.

B::C::D::a 3 // 11) cppreference says A::i is hidden by B::i so
             //     this == B::i == 3. Why does this go up the blocks
             //     D->C->B->B::i == 3 instead of the
             //     namespaces D->A->A::i == 1?
             //     This goes up the blocks like question 9 but not
             //     up the namespaces like question 10.
B::C::D::b 0 // 12) This probably goes up the namespaces D->A->A::b == 0 and not
             //     blocks D->C->B->B::b == 2 because b is accessed 
             //     outside the namespace body similar to questions 5, 7, and 8.
             //     Access inside the namespace body would be question 11 since
             //     the reference to i was captured inside a.
B::C::D::i 1 // 13) I think this is similar (~) to question 12 ~ 5, 7, and 8
             //     where it goes up namespaces D->A->A::i == 1 instead
             //     of blocks D->C->B->B::i == 3 because i is accessed outside
             //     of the namespace body.
B::C::D::j 5
B::C::D::k 6

List of questions that appeared above:

  1. Why does cppreference say A is injected into global and not D namespace?
  2. Why does cppreference say A is injected into global and not C namespace?
  3. What makes this ambiguous and not "one hides the other"?
  4. cppreference says A::i == 1 is hidden by B::i == 3 so this is == 3 and not 0. Why doesn't A::i hide B::i? Doesn't the using namespace A make A::i closer in scope than B::i? Why does this go up the parent blocks C->B->B::i == 1 and not up the namespaces C->D->A->A::i == 3?
  5. This is == A::b == 0. This goes through the using namespace D which contains using namespace A. Why does this go up the namespaces C->D->A->A::b == 0 and not the parent blocks to C->B->B::b == 2 like in question 4?
  6. This is == B::b == 2. Why is it not == B::C:b == 0 from the assignment int c = b? I'm guessing because name lookup for b inside the namespace body is different than the B::C::b lookup outside of the namespace body.
  7. Compared to question 9 below, i is assigned to m but 1 != 3. I think this is similar to question 6 where name lookup outside of the namespace body is different than inside. I'm not sure why this goes up namespaces C->D->A->A::i == 1 and not blocks C->B->B::i == 3.
  8. Why does it go up namespaces C->D->D::j and not blocks C->B->B::j == 4?
  9. cppreference says B::i hides A::i so this is == B::i == 3 Why does this go up blocks C->B->B::i == 3 and not namespaces C->D->A->A::i == 1? Actually, I guess questions 9 and 7 is the same situation as questions 6 and 5, respectively. Where m and i corresponds with c and b, respectively.
  10. cppreference says D::j hides B::j so this is == D::j == 5 Why does this go up namespaces C->D->D::j == 5 and not blocks C->B->B::j == 4? The only difference I see between question 9 and 10 is that for question 9, i isn't declared within D like j is.
  11. cppreference says A::i is hidden by B::i so this == B::i == 3. Why does this go up the blocks D->C->B->B::i == 3 instead of the namespaces D->A->A::i == 1? This goes up the blocks like question 9 but not up the namespaces like question 10.
  12. This probably goes up the namespaces D->A->A::b == 0 and not blocks D->C->B->B::b == 2 because b is accessed outside the namespace body similar to questions 5, 7, and 8. Access inside the namespace body would be question 11 since the reference to i was captured inside a.
  13. I think this is similar (~) to question 12 ~ 5, 7, and 8 where it goes up namespaces D->A->A::i == 1 instead of blocks D->C->B->B::i == 3 because i is accessed outside of the namespace body.
like image 777
dosentmatter Avatar asked Oct 17 '25 17:10

dosentmatter


1 Answers

// 1) Why does cppreference say A is injected into `global`
//    and not `D` namespace?
using namespace A; // all names from A injected into global namespace

Because global is the nearest enclosing namespace that contains both A and D. The effects of using namespace are explained on that same page, just above the example

using namespace D; // names from D are injected into C
// 2) Why does cppreference say A is injected into `global` and
//    not `C` namespace?
                  // names from A are injected into global namespace

Because global is the nearest enclosing namespace that contains both A and C

// 3) What makes this ambiguous and not "one hides the other"?
// int l = k;  // ambiguous: C::k or D::k

Because D::k was pulled into C by using namespace D;, and the effects of that are also explained just above the example.

Why doesn't A::i hide B::i? Doesn't the using namespace A make A::i closer in scope than B::i?

As noted above, using namespace A; pulled A::i into global namespace (when viewed from within that block). B::i is "closer in scope" than the global namespace.

In general, cppreference examples on language reference pages apply to the direcly preceding blocks of text, but it appears your main stumbling block is refusing to believe that using namespace A; that appears within the unrelated namespace D injects A's declarations into the global namespace. That's really what it does. That's what it's for.

like image 191
Cubbi Avatar answered Oct 19 '25 07:10

Cubbi