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

Могут ли члены класса быть определены за пределами пространства имен, в котором они объявлены?

Иногда я нахожу код как следующий (фактически некоторые мастера классов создают такой код):

// C.h
namespace NS {

class C {
    void f();
};

}

и в файле реализации:

// C.cpp
#include "C.h"

using namespace NS;
void C::f() {
  //...
}

Все компиляторы, которые я пробовал, принимают такой код (gcc, clang, msvc, compileonline.com). Что заставляет меня чувствовать себя некомфортно, это using namespace NS;. С моей точки зрения, C::f() живет в глобальном пространстве имен в среде, которая имеет неквалифицированный доступ к объектам, живущим в пространстве имен NS. Но в мнении компилятора void C::f() живет namespace NS. Поскольку все компиляторы я пробовал поделиться этой точкой зрения, они, вероятно, правы, но где в стандарте это мнение поддержано?

4b9b3361

Ответ 1

Да, синтаксис действительно легальный, но нет, ваша функция действительно живет в пространстве имен NS. Код, который вы видите, фактически эквивалентен

namespace NS { void C::f() { /* ... } }

или

void NS::C::f() { /* ... */ }

который может быть более похожим на то, к чему вы привыкли.

Из-за директивы using вы можете опустить часть NS не только в вызове кода, но и в его определении. В стандарте приведен пример, соответствующий вашему коду (после выделенной выделенной части):

3.4.3.2 Члены пространства имен [namespace.qual]

7 В объявлении для члена пространства имен, в котором идентификатор объявления является квалифицированным идентификатором, учитывая, что квалифицированный идентификатор для пространства имен член имеет форму nested-name-specifier unqualified-id unqualified-id должен указывать член пространства имен, обозначенного вложенный имя-спецификатор или элемент встроенного пространства имен (7.3.1) этого пространства имен. [Пример:

namespace A {
  namespace B {
    void f1(int);
  }
  using namespace B;
}
void A::f1(int){ } // ill-formed, f1 is not a member of A

-end example] Однако в таких объявлениях имен пространства имен Вложенное имя-спецификатор может неявно полагаться на использование директив укажите начальную часть спецификатора вложенных имен. [Пример:

namespace A {
  namespace B {
    void f1(int);
  }
}

namespace C {
  namespace D {
    void f1(int);
  }
}

using namespace A;
using namespace C::D;
void B::f1(int){ } // OK, defines A::B::f1(int)

-end пример]

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