C++ Вложенное `пространство имен` `используя` порядок поиска имен

Я читал о using-директивах на cppreference.com и у них был какой-то код, я не мог понять предпочтения порядка для поиска имени.

Я прочитал о транзитивном свойстве using-директив в параграфе 3< /a>, unqualified_lookup#область пространства имен и scope#namespace_scope. Я также пробовал искать на некоторых других сайтах.

Если есть еще какие-то документы, которые, по вашему мнению, мне следует прочитать, предложите их.

Их код следующий:

Не тратьте слишком много времени на чтение этого кода, потому что ниже я расскажу о своей адаптированной версии.

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
    }
}

Я адаптировал их код для вывода на печать:

Я помещаю пронумерованные вопросы в качестве комментариев к тем, которые я не понимаю. Вам не нужно отвечать на все вопросы, если вы можете объяснить порядок или поиск имени, и вы думаете, что я сам могу ответить на остальные вопросы.

Если мои вопросы слишком запутаны, не могли бы вы вместо этого попытаться объяснить поиск имен переменных в приведенном выше коде cppreference?

#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;
}

Полный вывод:

Я помещаю пронумерованные вопросы в качестве комментариев к тем, которые я не понимаю.

Я предлагаю вам открыть приведенный выше код рядом, чтобы вы могли видеть, на что я ссылаюсь. Я сохранил строки ‹ 80 символов.

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

Список вопросов, появившихся выше:

  1. Почему cppreference говорит, что A вводится в пространство имен global, а не D?
  2. Почему cppreference говорит, что A вводится в пространство имен global, а не C?
  3. Что делает это двусмысленным, а не "одно скрывает другое"?
  4. cppreference говорит, что A::i == 1 скрыто B::i == 3, поэтому это == 3, а не 0. Почему A::i не скрывает B::i? Разве using namespace A не делает A::i ближе по объему, чем B::i? Почему это поднимается вверх по родительским блокам C->B->B::i == 1, а не вверх по пространствам имен C->D->A->A::i == 3?
  5. Это == A::b == 0. Это проходит через using namespace D, который содержит using namespace A. Почему это поднимается вверх по пространствам имен C->D->A->A::b == 0, а не по родительским блокам до C->B->B::b == 2, как в вопросе 4?
  6. Это == B::b == 2. Почему это не == B::C:b == 0 из присваивания int c = b? Я предполагаю, что поиск имени для b внутри тела пространства имен отличается от поиска B::C::b вне тела пространства имен.
  7. По сравнению с вопросом 9 ниже, i присвоено m, но 1 != 3. Я думаю, что это похоже на вопрос 6, где поиск имени вне тела пространства имен отличается от поиска внутри. Я не уверен, почему это поднимается вверх по пространствам имен C->D->A->A::i == 1, а не блокирует C->B->B::i == 3.
  8. Почему он поднимается вверх по пространствам имен C->D->D::j, а не блокирует C->B->B::j == 4?
  9. cppreference говорит, что B::i скрывает A::i, так что это == B::i == 3 Почему это идет вверх по блокам C->B->B::i == 3 а не пространствам имен C->D- >А->А::i == 1? На самом деле, я думаю, вопросы 9 и 7 аналогичны вопросам 6 и 5 соответственно. Где m и i соответствуют c и b соответственно.
  10. cppreference говорит, что D::j скрывает B::j, так что это == D::j == 5 Почему это поднимается вверх по пространствам имен C->D->D::j == 5, а не блокирует C->B- >B::j == 4? Единственная разница, которую я вижу между вопросом 9 и 10, заключается в том, что для вопроса 9 я не объявлен в D, как j.
  11. cppreference говорит, что A::i скрыт B::i, поэтому это == B::i == 3. Почему это идет вверх по блокам D->C->B->B::i == 3 вместо пространства имен D->A->A::i == 1? Это идет вверх по блокам, как вопрос 9, но не вверх по пространствам имен, как вопрос 10.
  12. Это, вероятно, поднимается вверх по пространствам имен D->A->A::b == 0, а не блокирует D->C->B->B::b == 2, потому что доступ к b осуществляется вне тела пространства имен, аналогично вопросам 5. , 7 и 8. Доступ внутри тела пространства имен будет вопросом 11, поскольку ссылка на i была захвачена внутри a.
  13. Я думаю, что это похоже (~) на вопросы 12 ~ 5, 7 и 8, где поднимаются пространства имен D->A->A::i == 1 вместо блоков D->C->B->B: :i == 3, потому что доступ к i осуществляется за пределами тела пространства имен.

person dosentmatter    schedule 25.02.2018    source источник
comment
Единственное, что можно сказать наверняка, это то, что использование using namespace в пространстве имен вряд ли является хорошей идеей.   -  person user7860670    schedule 25.02.2018
comment
Я понимаю. Я не программист на С++, но я пытался прочитать код на С++, и мне пришлось прочитать using namespace, чтобы увидеть, как это работает. Потом я увидел этот пример и не мог уложиться в голове. Код не предназначен быть чем-то полезным, и я знаю, что это беспорядок. Это только в образовательных целях.   -  person dosentmatter    schedule 25.02.2018
comment
Вы присоединились к SO более года назад, пора совершить тур. Вопрос даже из двух частей рискует сделать ваш пост слишком широким. Список из 13 определенно слишком широк. Посетите справочный центр и сосредоточьтесь на Как спросить.   -  person StoryTeller - Unslander Monica    schedule 25.02.2018
comment
Спасибо за совет. Я чувствовал, что тур охватил вещи, которые я уже знал, но «Как спросить» был довольно полезным. Я согласен, что мой пост слишком длинный. Я просто пытался описать свой мыслительный процесс. Не глядя на ход моих мыслей, все вопросы на самом деле просто означают. Можете ли вы объяснить, как разрешается эта переменная? Объясните порядок предпочтения областей, чтобы я мог понять, почему было выбрано одно значение вместо другого. Если ответ может объяснить это для каждой переменной, то мой мыслительный процесс можно не принимать во внимание. Как вы думаете, как я могу уменьшить пост, не уменьшая код?   -  person dosentmatter    schedule 25.02.2018
comment
Еще немного: одно различие между использованием A и D заключается в том, что одно импортирует имена снаружи внутрь, а другое — изнутри наружу. Такие разные правила сокрытия имени. В остальном, например, импортировать A в D и затем извлекать содержимое D в C, это просто напоминает мне Доктор, Доктор, мне больно, когда я это делаю.   -  person Bo Persson    schedule 25.02.2018
comment
У вас есть ссылка на документацию о правилах скрытия имен при импорте извне внутрь и изнутри наружу? Да, у меня мозг болит, когда я думаю о коде cppreference, но я его увидел и хочу понять.   -  person dosentmatter    schedule 25.02.2018


Ответы (2)


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

Поскольку global — это ближайшее объемлющее пространство имен, содержащее как A, так и D. Эффекты using namespace объясняются на той же странице, прямо над примером.

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

Поскольку global — это ближайшее окружающее пространство имен, содержащее как A, так и C.

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

Потому что D::k был втянут в C using namespace D;, и последствия этого также объясняются чуть выше примера.

Почему A::i не скрывает B::i? Разве использование пространства имен A не делает A::i ближе по объему, чем B::i?

Как отмечалось выше, using namespace A; вытащил A::i в глобальное пространство имен (если смотреть изнутри этого блока). B::i "ближе по объему", чем глобальное пространство имен.

Как правило, примеры cppreference на страницах справки по языку относятся к непосредственно предшествующим блокам текста, но, похоже, ваш основной камень преткновения заключается в том, что вы отказываетесь верить, что using namespace A;, появляющееся в несвязанном пространстве имен, D внедряет объявления A в глобальное пространство имен. Это действительно то, что он делает. Вот для чего это нужно.

person Cubbi    schedule 06.03.2018
comment
Спасибо! Я прочитал блоки текста перед примером, но неправильно понял ближайшее окружающее пространство имен, которое содержит как директиву использования, так и имя пространства имен. Я думаю, по какой-то причине я интерпретировал это как using namespace A, это и директива использования, и она имеет namespace-name, что в данном случае A. Поэтому я всегда думал, что это всего лишь одно объемлющее пространство имен. - person dosentmatter; 08.03.2018

Стандарт C++17 (черновик здесь), [basic.lookup.unqual], гласит:

Объявления из пространства имен, назначенного директивой-использования, становятся видимыми в пространстве имен, включающем директиву-использования [и] считаются членами этого вмещающего пространства имен.

Позже в стандарте С++ 17 [namespace.udir] читается:

Директива-использования указывает, что имена в назначенном пространстве имен могут использоваться в области, в которой директива-использования появляется после директивы-использования. Во время поиска неквалифицированного имени имена отображаются так, как если бы они были объявлены в ближайшем охватывающем пространстве имен, которое содержит как директиву использования, так и назначенное пространство имен. Примечание. В данном контексте "содержит" означает "прямо или косвенно содержит". — конец примечания

Они могут хотя бы частично ответить на ваш вопрос.

Ваш пример кода тщательный. Если менее тщательный, но более краткий пример кода также может служить иллюстрацией, попробуйте следующее:

#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;
}

Вывод после компиляции GCC 6.3: 4200.

person thb    schedule 12.03.2019