Подтвердить что ты не робот

Почему директива use не "ассоциируется" с обычными функциями?

В соответствии с этим вопросом можно определить методы класса после директивы using, а не включать их в блок namespace.

Однако, похоже, это не относится к обычным функциям. Рассмотрим:

Greeting.hh

#pragma once

namespace NS
{
    class Greeting
    {
    public:
        void hello();
    };

    void otherHello();
}

Greeting.cc

#include "Greeting.hh"
#include <iostream>

using namespace NS;

void Greeting::hello()
{
    std::cout << "Greeting::hello" << std::endl;
}

void otherHello()
{
    std::cout << "otherHello" << std::endl;
}

main.cc

#include "Greeting.hh"

int main()
{
    NS::Greeting o;
    o.hello();
    NS::otherHello();
}

Это не будет компилироваться, что приведет к следующему сообщению об ошибке:

undefined reference to `NS::otherHello()'

Дальнейшая проверка указывает, что символу otherHello не предшествует пространство имен, а Greeting::hello:

g++ -std=c++14 -pedantic -Wall -c Greeting.cc
nm -C Greeting.o | grep T
000000000000002a T otherHello()
0000000000000000 T NS::Greeting::hello()

Это противоречит стандартной ссылке из принятого ответа?

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

4b9b3361

Ответ 1

Важно помнить, что

  • Объявления функций в разных пространствах имен не мешают друг другу.
  • Определение функции также является объявлением.
  • [namespace.def/4]

Вложенные пространства имен объявления являются теми пространствами имен в который декларация лексически появляется, за исключением переоформления член пространства имен за пределами своего исходного пространства имен (например, определение как указано в [namespace.memdef]). Такое переобучение имеет тот же заключая пространства имен в качестве первоначальной декларации.

Итак, давайте посмотрим на определение otherHello. Где это лексически появляется? Конечно, в глобальном пространстве имен. Это и точка декларации. Это означает, что охватывающее пространство имен является глобальным, и вы получите объявление ::otherHello.

Нет, это не противоречит стандартной цитате из принятого ответа на другой вопрос. Функции-члены могут быть определены вне класса, если они квалифицируются по их имени класса ([class.mfct/4]):

Если определение функции-члена лексически вне его класса определение, имя функции-члена должно быть квалифицировано его классом имя с помощью оператора::.

Итак, нам нужно только спросить, имеет ли Greeting имя тот же класс, что и NS::Greeting? Почему, да, это так. За это отвечает директива use.


Я добавлю этот сегмент в надежде выяснить. Рассмотрим этот фрагмент кода:

namespace NS1 {
    namespace NS2 {
        void hello();
    }
}

using namespace NS1;

void NS2::hello() {

}

int main() {
    NS1::NS2::hello();
    return 0;
}

Когда компилятор встречается с NS2::hello, он задает имя для этого идентификатора декларатора. Согласно [basic.lookup.qual/3]:

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

Итак, NS2 просматривается в области определения (глобальная), и в соответствии с неквалифицированным правилом поиска имени, которое вы указали, оно найдено и разрешается как NS1::NS2. То, что NS2::hello связано с NS1::NS2::hello и разрешено как определяющее его.

В глобальном пространстве имен OP, otherHello не предшествует ничего. Поэтому поиск имени не происходит. Он сразу же определяет эту функцию в охватывающем пространстве имен, как я уже цитировал.