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

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

// 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, Поскольку все компиляторы, которые я пытался использовать, разделяют эту точку зрения, они, вероятно, правы, но где в стандарте это мнение подкреплено?

14

Решение

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

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

или

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

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

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

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

7 В объявлении для члена пространства имен, в котором идентификатор объявления
является квалифицированным идентификатором, учитывая, что квалифицированный идентификатор для пространства имен
член имеет форму nested-name-specier unqualified-id
unqualified-id должен назвать члена пространства имен, обозначенного
nested-name-specifier или элемента встроенного пространства имен
(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

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

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)

— конец примера]

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

14

Другие решения


По вопросам рекламы [email protected]