Объявления пространства имен

Объявление пространства имен состоит из ключевого слова namespace, за которым следует имя и тело пространства имен, а затем точка с запятой (необязательно).

namespace-declaration:
namespace qualified-identifier namespace-body ;opt

qualified-identifier:
identifier
qualified-identifier . identifier

namespace-body:
{ extern-alias-directivesopt using-directivesopt namespace-member-declarationsopt }

Объявление пространства имен может быть объявлением верхнего уровня в единице компиляции или объявлением члена внутри другого объявления пространства имен. Если объявление пространства имен встречается как объявление верхнего уровня в единице компиляции, это пространство имен становится членом глобального пространства имен. Если объявление пространства имен встречается внутри другого объявления пространства имен, внутреннее пространство имен становится членом внешнего пространства имен. В обоих случаях имя пространства имен должно быть уникальным внутри содержащего пространства имен.

Пространства имен являются неявно открытыми (public); в объявление пространства имен не могут включаться модификаторы доступа.

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

Уточненный идентификатор объявления пространства имен может быть одиночным идентификатором или последовательностью идентификаторов, разделенных маркером «.». Последняя форма позволяет программе определять вложенные пространства имен без лексического вложения нескольких объявлений пространств имен. Например,

namespace N1.N2
{
class A {}

class B {}
}

семантически эквивалентно

namespace N1
{
namespace N2
{
class A {}

class B {}
}
}

Пространства имен являются открытыми, и два объявления пространств имен с одним и тем же полным именем могут размещаться в одну и ту же область объявлений (§3.3). В этом примере

namespace N1.N2
{
class A {}
}

namespace N1.N2
{
class B {}
}

два объявления пространств имен вверху размещаются в одной и той же области объявлений, объявляя в этом случае два класса с полными именами N1.N2.A и N1.N2.B. Поскольку эти два объявления размещаются в одной и той же области объявлений, было бы ошибкой, если бы в каждом содержалось объявление члена с одним и тем же именем.

Внешние псевдонимы

Директива extern alias вводит идентификатор, служащий псевдонимом пространства имен. Спецификация пространства имен с псевдонимом является внешней по отношению к исходному коду программы и применяется также к вложенным пространствам имен пространства имен с псевдонимом.

extern-alias-directives:
extern-alias-directive
extern-alias-directives extern-alias-directive

extern-alias-directive:
extern alias identifier ;

Область видимости директивы extern alias, распространяется на директивы using, глобальные атрибуты и объявления членов пространства имен незамедлительно содержащегося блока компиляции или тела пространства имен.

Внутри блока компиляции или тела пространства имен, содержащего директиву extern alias, идентификатор, введенный директивой extern alias, можно использовать для ссылки на пространство имен псевдонимов. Если в качестве идентификатора использовать слово global, произойдет ошибка времени компиляции.

Директива extern alias делает псевдоним доступным внутри отдельной единицы компиляции или тела пространства имен, но не размещает новые члены в базовой области объявлений. Иначе говоря, директива extern alias не является транзитивной, и влияет только на единицу компиляции или тело пространства имен, в котором находится.

В следующей программе объявляются и используются два внешних псевдонима, X и Y, каждый из которых представляет корень отдельной иерархии пространства имен:

extern alias X;
extern alias Y;

class Test
{
X::N.A a;
X::N.B b1;
Y::N.B b2;
Y::N.C c;
}

В программе объявлено существование внешних псевдонимов X и Y, но фактические определения этих псевдонимов являются внешними для этой программы. На классы N.B с одинаковыми именами теперь можно ссылаться как X.N.B и Y.N.B, или с помощью квалификатора псевдонима пространства имен как X::N.B и Y::N.B. Если в программе объявлен внешний псевдоним, для которого не дано внешнее определение, происходит ошибка.

Директивы using

Директивы using упрощают использование пространств имен и типов, определенных в других пространствах имен. Директивы using влияют на процесс разрешения имен пространств имен или типов (§3.8) и простых имен (§7.6.2), но, в отличие от объявлений, директивы using не размещают новые члены в базовых областях объявлений единиц компиляции или пространств имен, в которых они использованы.

using-directives:
using-directive
using-directives using-directive

using-directive:
using-alias-directive
using-namespace-directive

Директива using alias (§9.4.1) вводит псевдоним для пространства имен или типа.

Директива using namespace (§9.4.2) импортирует члены типа пространства имен.

Область директивы using распространяется на объявления членов пространства имен непосредственно содержащей ее единицы компиляции или тела пространства имен. Область видимости директивы using не включает одноуровневые директивы using. Поэтому одноуровневые директивы using не влияют друг на друга и могут быть записаны в любом порядке.

Директивы using alias

Директива using alias вводит идентификатор, служащий псевдонимом для пространства имен или типа внутри непосредственно вмещающей единицы компиляции или тела пространства имен.

using-alias-directive:
using identifier = namespace-or-type-name ;

Внутри объявлений членов в единице компиляции или в теле пространства имен, содержащем директиву using alias, идентификатор, введенный директивой using alias, можно использовать для ссылки на данное пространство имен или тип. Пример:

namespace N1.N2
{
class A {}
}

namespace N3
{
using A = N1.N2.A;

class B: A {}
}

В приведенном примере внутри объявлений членов в пространстве имен N3, A является псевдонимом для N1.N2.A, и таким образом класс N3.B является производным от класса N1.N2.A. Такой же результат можно получить, создав псевдоним R для N1.N2 и затем ссылаясь на R.A:

namespace N3
{
using R = N1.N2;

class B: R.A {}
}

Идентификатор в директиве using alias должен быть уникальным внутри области объявлений единицы компиляции или пространства имен, непосредственно содержащих директиву using alias. Пример:

namespace N3
{
class A {}
}

namespace N3
{
using A = N1.N2.A; // Error, A already exists
}

В приведенном примере N3 уже содержит член A, поэтому использование этого идентификатора в директиве using alias вызовет ошибку времени компиляции. Аналогично, произойдет ошибка времени компиляции, если две или более директивы using alias в одной и той же единице компиляции или пространстве имен объявляют псевдонимы с одним и тем же именем.

Директива using alias делает псевдоним доступным внутри отдельной единицы компиляции или тела пространства имен, но не размещает новые члены в базовой области объявлений. Иначе говоря, директива using alias не является транзитивной, и влияет только на единицу компиляции или тело пространства имен, в котором находится. В этом примере

namespace N3
{
using R = N1.N2;
}

namespace N3
{
class B: R.A {} // Error, R unknown
}

область директивы using alias, которая вводит R, распространяется только на объявления членов в теле пространства имен, в котором она содержится, так что R неизвестно в объявлении второго пространства имен. Однако если поместить директиву using alias в содержащую единицу компиляции, то этот псевдоним будет доступен внутри обоих объявлений пространств имен:

using R = N1.N2;

namespace N3
{
class B: R.A {}
}

namespace N3
{
class C: R.A {}
}

Как и регулярные члены, имена, введенные директивой using alias, скрыты членами с таким же именем во вложенных областях. В этом примере

using R = N1.N2;

namespace N3
{
class R {}

class B: R.A {} // Error, R has no member A
}

ссылка на R.A в объявлении B приводит к ошибке времени компиляции, так как R ссылается на N3.R, а не на N1.N2.

Порядок, в котором записаны директивы using alias, не имеет значения, а на разрешение имени пространства имен или типа, на которое ссылается директива using alias, не влияет ни сама директива using alias, ни другие директивы using в непосредственно содержащей единице компиляции или теле пространства имен. Другими словами, имя типа или пространства имен директивы using alias разрешается точно так же, как если бы непосредственно содержащие директиву блок компиляции или тело пространства имен не имели директив using. Однако на директиву using alias могут повлиять директивы extern alias из непосредственно содержащего блока компиляции или тела пространства имен. В этом примере

namespace N1.N2 {}

namespace N3
{
extern alias E;

using R1 = E.N; // OK

using R2 = N1; // OK

using R3 = N1.N2; // OK

using R4 = R2.N2; // Error, R2 unknown
}

последняя директива using alias приводит к ошибке времени компиляции, так как на нее не влияет первая директива using alias. Первая директива using alias не приводит к ошибке, поскольку область внешнего псевдонима E включает директиву using alias.

Директивой using alias можно создать псевдоним для любого пространства имен или типа, включая пространство имен, внутри которого она находится, и любое пространство имен или тип, вложенные в это пространство имен.

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

namespace N1.N2
{
class A {}
}

namespace N3
{
using R1 = N1;
using R2 = N1.N2;

class B
{
N1.N2.A a; // refers to N1.N2.A
R1.N2.A b; // refers to N1.N2.A
R2.A c; // refers to N1.N2.A
}
}

имена N1.N2.A, R1.N2.A и R2.A эквивалентны и все они ссылаются на класс, полное имя которого N1.N2.A.

С помощью псевдонимов можно именовать закрытый сформированный тип, но нельзя именовать объявление несвязанного универсального типа, не предоставляя аргументы типа. Пример:

namespace N1
{
class A<T>
{
class B {}
}
}

namespace N2
{
using W = N1.A; // Error, cannot name unbound generic type

using X = N1.A.B; // Error, cannot name unbound generic type

using Y = N1.A<int>; // Ok, can name closed constructed type

using Z<T> = N1.A<T>; // Error, using alias cannot have type parameters
}

Директивы using namespace

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

using-namespace-directive:
using namespace-name ;

Внутри объявлений членов в единице компиляции или теле пространства имен, содержащем директиву using namespace, на типы в данном пространстве имен можно ссылаться непосредственно. Пример:

namespace N1.N2
{
class A {}
}

namespace N3
{
using N1.N2;

class B: A {}
}

В данном примере внутри объявлений членов в пространстве имен N3 члены типа N1.N2 доступны непосредственно, и таким образом класс N3.B является производным от класса N1.N2.A.

Директива using namespace импортирует типы, содержащиеся в данном пространстве имен, но не импортирует вложенные пространства имен. В этом примере

namespace N1.N2
{
class A {}
}

namespace N3
{
using N1;

class B: N2.A {} // Error, N2 unknown
}

директива using namespace импортирует типы, содержащиеся в N1, но не пространства имен, вложенные в N1. Таким образом, ссылка на N2.A в объявлении B приводит к ошибке времени компиляции, так как в области нет членов с именем N2.

В отличие от директивы using alias, директива using namespace может импортировать типы, идентификаторы которых уже определены внутри вмещающей единицы компиляции или тела пространства имен. В действительности, имена, импортированные директивой using namespace, скрыты членами с тем же именем во вмещающей единице компиляции или теле пространства имен. Пример:

namespace N1.N2
{
class A {}

class B {}
}

namespace N3
{
using N1.N2;

class A {}
}

В данном случае внутри объявлений членов в пространстве имен N3, A ссылается на N3.A, а не на N1.N2.A.

Если более одного пространства имен, импортированных директивами using namespace в одну и ту же единицу компиляции или тело пространства имен, содержат типы с одинаковым именем, ссылки на эти типы считаются неоднозначными. В этом примере

namespace N1
{
class A {}
}

namespace N2
{
class A {}
}

namespace N3
{
using N1;

using N2;

class B: A {} // Error, A is ambiguous
}

N1 и N2 содержат член A, а поскольку N3 импортирует оба, ссылка на A в N3 дает ошибку времени компиляции. В этой ситуации можно разрешить конфликт либо квалифицируя ссылки на A, либо введя директиву using alias, выбирающую отдельный A. Пример:

namespace N3
{
using N1;

using N2;

using A = N1.A;

class B: A {} // A means N1.A
}

Как и директива using alias, директива using namespace не размещает новые члены в базовой области объявлений единицы компиляции или пространства имен, а влияет только на единицу компиляции или тело пространства имен, в котором находится.

Имя пространства имен, на которое ссылается директива using namespace, разрешается таким же образом, как имя пространства имен или типа, на которое ссылается директива using alias. Таким образом, директивы using namespace в одной и той же единице компиляции или теле пространства имен не влияют друг на друга и могут быть записаны в любом порядке.

Члены пространства имен

Объявление члена пространства имен является либо объявлением пространства имен (§9.2), либо объявлением типа (§9.6).

namespace-member-declarations:
namespace-member-declaration
namespace-member-declarations namespace-member-declaration

namespace-member-declaration:
namespace-declaration
type-declaration

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

Объявления типов

Объявление типа является объявлением класса (§10.1), объявлением структуры (§11.1), объявлением интерфейса (§13.1), объявлением перечисляемого типа (§14.1) или объявлением делегата (§15.1).

type-declaration:
class-declaration
struct-declaration
interface-declaration
enum-declaration
delegate-declaration

Объявление типа может быть объявлением верхнего уровня в единице компиляции или объявлением члена внутри пространства имен, класса или структуры.

Если объявление типа для типа T является объявлением верхнего уровня в единице компиляции, полное имя вновь объявленного типа просто T. Если объявление типа для типа T находится внутри пространства имен, класса или структуры, то полное имя вновь объявленного типа N.T, где N является полным именем содержащего пространства имен, класса или структуры.

Тип, объявленный внутри класса или структуры, называется вложенным типом (§10.3.8).

Разрешенные модификаторы доступа и доступ по умолчанию для объявления типа зависит от контекста, в котором имеет место объявление (§3.5.1):

· типы, объявленные в единицах компиляции или в пространствах имен, могут иметь доступ public или internal. По умолчанию — доступ internal.

· типы, объявленные в классах, могут иметь доступ public, protected internal, protected internal или private. По умолчанию — доступ private.

· типы, объявленные в структурах, могут иметь доступ public, internal или private. По умолчанию — доступ private.

Наши рекомендации