Иногда я нахожу код, подобный следующему (на самом деле некоторые мастера классов создают такой код):
// 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
, Поскольку все компиляторы, которые я пытался использовать, разделяют эту точку зрения, они, вероятно, правы, но где в стандарте это мнение подкреплено?
Да, синтаксис действительно законный, но нет, ваша функция на самом деле живет в пространстве имен 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)
— конец примера]
Таким образом, вы можете опустить начальная часть спецификатора вложенного имени, но не какая-либо промежуточная часть.