What is the scope of a namespace identifier?

It seems that there is no clear definition over the point of declaration, declarative region, scope of a namespace identifier, except those of an identifier inside a namespace—according to the standard(§3.3.6/1).

The declarative region of a namespace-definition is its namespace-body. The potential scope denoted by an original-namespace-name is the concatenation of the declarative regions established by each of the namespace-definitions in the same...

Although the standard indeed talks about those of a declaration—a namespace-definition is declaration, that is not applicable to the case of namespace-definition because it has no declarator, nor initializer—according to the standard(§3.3.2/1).

The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below...

Then, how can I determine those of a namespace identifier?

Upvotes: 13

Views: 2738

Answers (5)

Aditya Gupta
Aditya Gupta

Reputation: 1

Let's break it down to understand points of declaration, declarative regions, and scopes of namespace identifiers in C++:

Point of Declaration: For namespace identifiers, the point of declaration is immediately after the namespace definition. In other words, it's where the namespace is defined in the code.

Declarative Region: The declarative region of a namespace-definition is its entire namespace-body. This means that all the declarations and definitions within the namespace-body are part of the same declarative region.

Scope: The potential scope denoted by a namespace identifier includes all the code within the namespace-body. This means that any identifiers declared within the namespace can be accessed within that namespace.

#include <iostream>

namespace MyNamespace {
    int x;
    
    void foo() {
        std::cout << "Inside MyNamespace::foo" << std::endl;
    }
}

int main() {
    MyNamespace::x = 10;
    MyNamespace::foo();
    return 0;
}

In this example:

  • The point of declaration for the namespace identifier MyNamespace is immediately after the namespace MyNamespace { line.
  • The declarative region of MyNamespace includes everything within the curly braces {} following the namespace MyNamespace line.
  • The scope of MyNamespace encompasses all code within its namespace-body, so both x and foo() can be accessed using the MyNamespace:: scope resolution operator.

Upvotes: -2

Davis Herring
Davis Herring

Reputation: 39818

P1787R6 addresses this as part of regularizing the name-lookup rules in general: now [basic.scope.scope] defines general terms like "target scope" that establish the context for a namespace-definition (and [basic.namespace.general]/3 addresses the special case of the global namespace), and [basic.scope.pdecl]/14 specifically addresses the locus (which replaced "point of declaration") for a namespace.

Upvotes: 0

Peter
Peter

Reputation: 36597

From the text you quoted from the standard, my interpretation would be that you have answered your own question.

As you say, a namespace cannot have a complete declarator, since an additional declarative region for it can be created in any compilation unit (i.e. source file or header included by that source file) for any namespace X by namespace X { <new names within this declarative region> }.

Since there can never be a complete declaration of a namespace, there can never be a point of declaration for a namespace. Since there is no point of declaration, there is no such thing as a namespace identifier, and no such thing as a scope of one.

Which means that a namespace is just a label which can be part of an identifier. istream is an identifier within namespace std, the complete name of that identifier (as referenced from code outside a declarative region of namespace std) is std::istream. All the using namespace std; does is, when trying to find a match for a potential identifier foo is tell the compiler to look within namespace std (or the declarative regions it has visibility of) for an identifier named foo which will be a candidate match. [Which is why using namespace for multiple namespaces can result in ambiguity, if more than one namespace contains the same identifier].

Upvotes: 3

Francis Cugler
Francis Cugler

Reputation: 7905

I will try to break this down into parts based on the misunderstanding of the op.

He stated:

It seems that there is no clear definition over the point of declaration, declarative region, scope of a namespace identifier

// File A.h
namespace foo {        // Resides At Global Scope
    // some code here.
    void bar();        // Resided At foo:: Scope
}

// File A.cpp
namespace foo {
    // some more code here
    void bar() { 
        std::cout << "Hell World" << std::endl; 
    }
}

Here the declarative region spans multiple files for foo its definition and declaration is foo and foo is its identifier. Since this is a named namespace there is no declarator as you would see with a variable, function or type as defined in section 8.1

A declarator declares a single variable, function, or type, within a declaration

And there is no initializer either since it is a namespace. It is a declarative region that has scope.

And what I believe that the op is asking is this:

Then, how can I determine those of a namespace identifier?

The namespace identifier is the name of the namespace which is also its definition and declaration. In a sense all 3 are one and the same, and the declarative region of a namespace and its scope is one and the same.

No, the standard doesn't draw a clear picture of the point of declaration for a namespace because it refers to the use of a declator which a namespace doesn't have. In some cases a namespace can be initialized by another namespace upon its definition - declaration as in: Typically an alias!

namespace someLongName{
}

namespace shortVersion = someLongName {
}

Where the point of declaration is found in 3.3.2.1, If you continue through this section in secession it moves on to Block Scope, Function Prototype Scope, Function Scope, Namespace Scope, Class Scope, Enumeration Scope, Template Parameter Scope, however if you refer back to 3.1 under definitions of Declarations and Definitions; a name space is defined and not declared, but in section 3.3.6

The declarative region of a namespace-definition is its namespace-body. Entities declared in a namespace-body are said to be members of the namespace, and names introduced by these declarations into the declarative region of the namespace are said to be member names of the namespace. A namespace member name has namespace scope. Its potential scope includes its namespace from the name’s point of declaration (3.3.2) onwards; and for each using-directive (7.3.4) that nominates the member’s namespace, the member’s potential scope includes that portion of the potential scope of the using-directive that follows the member’s point of declaration.

and

7.3 1 & 2

7.3 Namespaces [basic.namespace] 1 A namespace is an optionally-named declarative region. The name of a namespace can be used to access entities declared in that namespace; that is, the members of the namespace. Unlike other declarative regions, the definition of a namespace can be split over several parts of one or more translation units. 2 The outermost declarative region of a translation unit is a namespace; see 3.3.6.

and

7.3.1 2-4

2 Every namespace-definition shall appear in the global scope or in a namespace scope (3.3.6). 3 In a named-namespace-definition, the identifier is the name of the namespace. If the identifier, when looked up (3.4.1), refers to a namespace-name (but not a namespace-alias) introduced in the declarative region in which the named-namespace-definition appears, the namespace-definition extends the previously-declared namespace. Otherwise, the identifier is introduced as a namespace-name into the declarative region in which the named-namespace-definition appears. 4 Because a namespace-definition contains declarations in its namespace-body and a namespace-definition is itself a declaration, it follows that namespace-definitions can be nested.

Here I'll try to suggest even without a clear definition that the point of declaration for a namespace is immediately after its identifier - definition - declaration!

namespace /*keyword*/ std /*definition, declaration, identifier*/ (point of declaration) { /*beginning of declarative region & scope*/  
    .... // some code

} // End of declarative region or scope until another matching identifier at same level of scope is found. 
  // And this can span multiple translations units.

I hope this clears up the confusion.

Upvotes: 1

Francis Cugler
Francis Cugler

Reputation: 7905

Based on your initial assumption:

It seems that there is no clear definition over the point of declaration, declarative region, scope of a namespace identifier, except those of an identifier inside a namespace—according to the standard(§3.3.6/1).

I was reading this from the standard found at open-std.org/pdf

3.3.6 Namespace scope [basic.scope.namespace] 1 The declarative region of a namespace-definition is its namespace-body. Entities declared in a namespace-body are said to be members of the namespace, and names introduced by these declarations into the declarative region of the namespace are said to be member names of the namespace. A namespace member name has namespace scope. Its potential scope includes its namespace from the name’s point of declaration (3.3.2) onwards; and for each using-directive (7.3.4) that nominates the member’s namespace, the member’s potential scope includes that portion of the potential scope of the using-directive that follows the member’s point of declaration. [ Example:

namespace N { 
    int i; 
    int g(int a) { return a; } 
    int j(); 
    void q(); 
} 

namespace { int l=1; } 
// the potential scope of l is from its point of declaration 
// to the end of the translation unit 

namespace N { 
    int g(char a) { // overloads N::g(int)
        return l+a; // l is from unnamed namespace 
    } 

    int i; // error: duplicate definition 
    int j(); // OK: duplicate function declaration

    int j() { // OK: definition of N::j() 
        return g(i); // calls N::g(int)
    } 
    int q(); // error: different return type 
} 

—end example ]

Which lead me to read the following sections:

3.3

3.3 Scope [basic.scope] 3.3.1 Declarative regions and scopes [basic.scope.declarative] 1 Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity. In general, each particular name is valid only within some possibly discontiguous portion of program text called its scope. To determine the scope of a declaration, it is sometimes convenient to refer to the potential scope of a declaration. The scope of a declaration is the same as its potential scope unless the potential scope contains another declaration of the same name. In that case, the potential scope of the declaration in the inner (contained) declarative region is excluded from the scope of the declaration in the outer (containing) declarative region. 2 [ Example: in

int j = 24;
int main() {
    int i = j, j;
    j = 42;
}

the identifier j is declared twice as a name (and used twice). The declarative region of the first j includes the entire example. The potential scope of the first j begins immediately after that j and extends to the end of the program, but its (actual) scope excludes the text between the , and the }. The declarative region of the second declaration of j (the j immediately before the semicolon) includes all the text between { and }, but its potential scope excludes the declaration of i. The scope of the second declaration of j is the same as its potential scope. —end example ] 3 The names declared by a declaration are introduced into the scope in which the declaration occurs, except that the presence of a friend specifier (11.3), certain uses of the elaborated-type-specifier (7.1.6.3), and using-directives (7.3.4) alter this general behavior. 4 Given a set of declarations in a single declarative region, each of which specifies the same unqualified name, (4.1) — they shall all refer to the same entity, or all refer to functions and function templates; or (4.2) — exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same variable or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden (3.3.10). [ Note: A namespace name or a class template name must be unique in its declarative region (7.3.2, Clause 14). —end note ] § 3.3.1 38

c ISO/IEC N4527 [ Note: These restrictions apply to the declarative region into which a name is introduced, which is not necessarily the same as the region in which the declaration occurs. In particular, elaborated-type-specifiers (7.1.6.3) and friend declarations (11.3) may introduce a (possibly not visible) name into an enclosing namespace; these restrictions apply to that region. Local extern declarations (3.5) may introduce a name into the declarative region where the declaration appears and also introduce a (possibly not visible) name into an enclosing namespace; these restrictions apply to both regions. —end note ] 5 [ Note: The name lookup rules are summarized in 3.4. —end note ]

3.1

3.1 Declarations and definitions [basic.def] 1 A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. If so, the declaration specifies the interpretation and attributes of these names. A declaration may also have effects including: (1.1) — a static assertion (Clause 7), (1.2) — controlling template instantiation (14.7.2), (1.3) — use of attributes (Clause 7), and (1.4) — nothing (in the case of an empty-declaration). § 3.1 33

c ISO/IEC N4527 2 A declaration is a definition unless it declares a function without specifying the function’s body (8.4), it contains the extern specifier (7.1.1) or a linkage-specification25 (7.5) and neither an initializer nor a functionbody, it declares a static data member in a class definition (9.2, 9.4), it is a class name declaration (9.1), it is an opaque-enum-declaration (7.2), it is a template-parameter (14.1), it is a parameter-declaration (8.3.5) in a function declarator that is not the declarator of a function-definition, or it is a typedef declaration (7.1.3), an alias-declaration (7.1.3), a using-declaration (7.3.3), a static_assert-declaration (Clause 7), an attributedeclaration (Clause 7), an empty-declaration (Clause 7), a using-directive (7.3.4), an explicit instantiation declaration (14.7.2), or an explicit specialization (14.7.3) whose declaration is not a definition. [ Example: all but one of the following are definitions:

int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x+a; } // defines f and defines x
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
    int x; // defines non-static data member x
    static int y; // declares static data member y
    X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up, down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX

whereas these are just declarations:

extern int a; // declares a
extern const int c; // declares c
int f(int); // declares f
struct S; // declares S
typedef int Int; // declares Int
extern X anotherX; // declares anotherX
using N::d; // declares d

—end example ] 3 [ Note: In some circumstances, C++ implementations implicitly define the default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4) member functions. —end note ] [ Example: given

#include <string>

struct C {
    std::string s; // std::string is the standard library class (Clause 21)
};

int main() {
    C a;
    C b = a;
    b = a;
}

the implementation will implicitly define functions to make the definition of C equivalent to 25) Appearing inside the braced-enclosed declaration-seq in a linkage-specification does not affect whether a declaration is a definition. § 3.1 34

c ISO/IEC N4527

struct C {
    std::string s;
    C() : s() { }
    C(const C& x): s(x.s) { }
    C(C&& x): s(static_cast<std::string&&>(x.s)) { }
        // : s(std::move(x.s)) { }
    C& operator=(const C& x) { s = x.s; return *this; }
    C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
        // { s = std::move(x.s); return *this; }
    ~C() { }
};

—end example ] 4 [ Note: A class name can also be implicitly declared by an elaborated-type-specifier (7.1.6.3). —end note ] 5 A program is ill-formed if the definition of any object gives the object an incomplete type (3.9).

Then you precede to state:

Although the standard indeed talks about those of a declaration—a namespace-definition is declaration, that is not applicable to the case of namespace-definition because it has no declarator, nor initializer—according to the standard(§3.3.2/1).

Then I read this:

3.3.2 Point of declaration [basic.scope.pdecl] 1 The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below. [ Example:

unsigned char x = 12;
{ unsigned char x = x; }

Here the second x is initialized with its own (indeterminate) value. —end example ]

and then you quote this:

The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any), except as noted below...

I finally read this:

8 Declarators [dcl.decl] 1 A declarator declares a single variable, function, or type, within a declaration. The init-declarator-list appearing in a declaration is a comma-separated sequence of declarators, each of which can have an initializer. init-declarator-list: init-declarator init-declarator-list , init-declarator init-declarator: declarator initializeropt 2 The three components of a simple-declaration are the attributes (7.6), the specifiers (decl-specifier-seq; 7.1) and the declarators (init-declarator-list). The specifiers indicate the type, storage class or other properties of the entities being declared. The declarators specify the names of these entities and (optionally) modify the type of the specifiers with operators such as * (pointer to) and () (function returning). Initial values can also be specified in a declarator; initializers are discussed in 8.5 and 12.6. 3 Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself.100 4 Declarators have the syntax declarator: ptr-declarator noptr-declarator parameters-and-qualifiers trailing-return-type ptr-declarator: noptr-declarator ptr-operator ptr-declarator noptr-declarator: declarator-id attribute-specifier-seqopt noptr-declarator parameters-and-qualifiers noptr-declarator [ constant-expressionopt] attribute-specifier-seqopt ( ptr-declarator ) parameters-and-qualifiers: ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt 100) A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator. That is T D1, D2, ... Dn; is usually equivalent to T D1; T D2; ... T Dn; where T is a decl-specifier-seq and each Di is an init-declarator. An exception occurs when a name introduced by one of the declarators hides a type name used by the decl-specifiers, so that when the same decl-specifiers are used in a subsequent declaration, they do not have the same meaning, as in struct S ... ; S S, T; // declare two instances of struct S which is not equivalent to struct S ... ; S S; S T; // error Another exception occurs when T is auto (7.1.6.4), for example: auto i = 1, j = 2.0; // error: deduced types for i and j do not match as opposed to auto i = 1; // OK: i deduced to have type int auto j = 2.0; // OK: j deduced to have type double Declarators 190

c ISO/IEC N4527 trailing-return-type: -> trailing-type-specifier-seq abstract-declaratoropt ptr-operator: * attribute-specifier-seqopt cv-qualifier-seqopt & attribute-specifier-seqopt && attribute-specifier-seqopt nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt cv-qualifier-seq: cv-qualifier cv-qualifier-seqopt cv-qualifier: const volatile ref-qualifier: & && declarator-id: ...opt id-expression 5 The optional attribute-specifier-seq in a trailing-return-type appertains to the indicated return type. The type-id in a trailing-return-type includes the longest possible sequence of abstract-declarators. [ Note: This resolves the ambiguous binding of array and function declarators. [ Example: auto f()->int(*)[4]; // function returning a pointer to array[4] of int // not function returning array[4] of pointer to int —end example ] —end note ]

You finally ask this:

Then, how can I determine those of a namespace identifier?

Did you proceed to read this section?

7.3 Namespaces [basic.namespace] 1 A namespace is an optionally-named declarative region. The name of a namespace can be used to access entities declared in that namespace; that is, the members of the namespace. Unlike other declarative regions, the definition of a namespace can be split over several parts of one or more translation units. 2 The outermost declarative region of a translation unit is a namespace; see 3.3.6. 7.3.1 Namespace definition [namespace.def] 1 The grammar for a namespace-definition is namespace-name: identifier namespace-alias namespace-definition: named-namespace-definition unnamed-namespace-definition nested-namespace-definition named-namespace-definition: inlineopt namespace attribute-specifier-seqopt identifier { namespace-body } unnamed-namespace-definition: inlineopt namespace attribute-specifier-seqopt{ namespace-body } nested-namespace-definition: namespace enclosing-namespace-specifier :: identifier { namespace-body } enclosing-namespace-specifier: identifier enclosing-namespace-specifier :: identifier § 7.3.1 168

c ISO/IEC N4527 namespace-body: declaration-seqopt 2 Every namespace-definition shall appear in the global scope or in a namespace scope (3.3.6). 3 In a named-namespace-definition, the identifier is the name of the namespace. If the identifier, when looked up (3.4.1), refers to a namespace-name (but not a namespace-alias) introduced in the declarative region in which the named-namespace-definition appears, the namespace-definition extends the previously-declared namespace. Otherwise, the identifier is introduced as a namespace-name into the declarative region in which the named-namespace-definition appears. 4 Because a namespace-definition contains declarations in its namespace-body and a namespace-definition is itself a declaration, it follows that namespace-definitions can be nested. [ Example:

namespace Outer {
    int i;
    namespace Inner {
        void f() { i++; } // Outer::i
        int i;
        void g() { i++; } // Inner::i
    }
}

—end example ] 5 The enclosing namespaces of a declaration are those namespaces in which the declaration lexically appears, except for a redeclaration of a namespace member outside its original namespace (e.g., a definition as specified in 7.3.1.2). Such a redeclaration has the same enclosing namespaces as the original declaration. [ Example:

namespace Q {
    namespace V {
        void f(); // enclosing namespaces are the global namespace, Q, and Q::V
        class C { void m(); };
    }
    void V::f() { // enclosing namespaces are the global namespace, Q, and Q::V
        extern void h(); // ... so this declares Q::V::h
    }
    void V::C::m() { // enclosing namespaces are the global namespace, Q, and Q::V
    }
}

—end example ] 6 If the optional initial inline keyword appears in a namespace-definition for a particular namespace, that namespace is declared to be an inline namespace. The inline keyword may be used on a namespacedefinition that extends a namespace only if it was previously used on the namespace-definition that initially declared the namespace-name for that namespace. 7 The optional attribute-specifier-seq in a named-namespace-definition appertains to the namespace being defined or extended. 8 Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace. Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used in argument-dependent lookup (3.4.2) whenever one of them is, and a usingdirective (7.3.4) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace (7.3.1.1). Furthermore, each member of the inline namespace can subsequently be partially specialized (14.5.5), explicitly instantiated (14.7.2), or explicitly specialized (14.7.3) as though it were a member of the enclosing namespace. Finally, looking up a name in the enclosing namespace via § 7.3.1 169

c ISO/IEC N4527 explicit qualification (3.4.3.2) will include members of the inline namespace brought in by the using-directive even if there are declarations of that name in the enclosing namespace. 9 These properties are transitive: if a namespace N contains an inline namespace M, which in turn contains an inline namespace O, then the members of O can be used as though they were members of M or N. The inline namespace set of N is the transitive closure of all inline namespaces in N. The enclosing namespace set of O is the set of namespaces consisting of the innermost non-inline namespace enclosing an inline namespace O, together with any intervening inline namespaces. 10 A nested-namespace-definition with an enclosing-namespace-specifier E, identifier I and namespace-body B is equivalent to namespace E { namespace I { B } } [ Example:

namespace A::B::C {
    int i;
}

The above has the same effect as:

namespace A {
    namespace B {
        namespace C {
            int i;
        }
    }
}

—end example ]

I think the lines of importance that answers your question is found from its definition 7.3 - 1 & 2 and 7.3.1 - 2,3 & 4. Provided you are working with a named namespace and you already understand scope, translation unit, declaration, definition and declarative space. There is no mention of a declarator of a name space in the declarator section because it is listed in the declaration section under the namespace section because the named namespace or its definition is its declaration, thus the identifier is the name of the namespace itself or its definition - declaration.

Upvotes: 1

Related Questions