4

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.
dosentmatter
  • 1,494
  • 1
  • 16
  • 23
  • 1
    The only thing that is certain is that using `using namespace` in namespace scope is hardly a good idea. – user7860670 Feb 25 '18 at 07:55
  • I understand. I'm not a c++ programmer but I was trying to read some c++ code and had to read up on `using namespace` to see how it works. Then I saw this example and couldn't wrap my head around it. The code isn't meant to be something useful and I know it's a mess. This is just for educational purposes. – dosentmatter Feb 25 '18 at 08:00
  • 1
    You joined SO over a year ago, it's high time to take the [tour]. Having even a two part question is at a risk of making your post too broad. Having a list of 13 is definitely too broad. Please visit the [help] and focus on [ask]. – StoryTeller - Unslander Monica Feb 25 '18 at 08:25
  • Thanks for the advice. I felt like the tour covered things I already knew but the How to Ask was kinda useful. I do agree that my post is way too long. I was just trying to describe my thought process. Without looking at my thought process, all of the questions really just mean "Can you explain how this variable is resolved? Explain the order of preference of the scopes so I can understand why it chose one value over the other." If an answer can explain that for each variable, then my thought process can be disregarded. How do you think I can shrink the post down without shrinking the code? – dosentmatter Feb 25 '18 at 08:39
  • 1
    One little bit: One difference between using A or using D is that one is importing names from the outside in, and the other from the inside out. So different name-hiding rules. For the rest, like importing A into D and then extracting D contents into C, it just reminds me of *"Doctor, Doctor, it hurts when I do this."* – Bo Persson Feb 25 '18 at 08:50
  • Do you have a link to documentation about name-hiding rules when importing from outside in vs inside out? Yeah, my brain hurts from thinking about the cppreference code but I saw it and want to understand it. – dosentmatter Feb 25 '18 at 09:01

2 Answers2

3
// 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.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • Thanks! I read the blocks of text before the example but I misunderstood "nearest enclosing namespace which contains both the using-directive and namespace-name". I think, for some reason, I interpreted it as `using namespace A` is both a using-directive and it has `namespace-name`, which is `A` in this case. So I thought it was just one enclosing namespace all the time. – dosentmatter Mar 08 '18 at 00:48
1

The C++17 standard (draft here), [basic.lookup.unqual], reads:

The declarations from the namespace nominated by a using-directive become visible in a namespace enclosing the using-directive [and] are considered members of that enclosing namespace.

Later in the C++17 standard, [namespace.udir] reads:

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, 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". — end note

These might at least partly answer your question.

Your sample code is thorough. If a less thorough but briefer sample code would also serve to illustrate, then try this:

#include <iostream>

namespace Your {
    int f(const int n) {return 10*n;}
}

namespace My {
    int f(const int n) {return 100*n;}
    namespace Our {
        using namespace Your;
        int g() {return f(42);} // lookup from here
    }
}

int main()
{
    std::cout << My::Our::g() << "\n";
    return 0;
}

Output after compilation by GCC 6.3: 4200.

thb
  • 13,796
  • 3
  • 40
  • 68