%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** %% # 命名空间 **命名空间作用域** 中声明 or 定义的实体具有以下特性: 1. 具有 "**==静态存储持续性==**"。 2. 默认具有"**==外部链接性==**",即可以跨翻译单元访问。 > [!NOTE] "**全局作用域**" 本质上是 "**全局命名空间作用域**" <br><br> # 全局命名空间 **全局命名空间(global namespace)** 是隐式存在的命名空间,对应于**文件级声明区域**,也称 "**==全局作用域==**" [^1]。 所有文件级声明区域下(即**不在任何函数、类或其它命名空间内部**)的声明和定义都属于全局命名空间,例如**全局变量、函数、类**。 全局命名空间没有名称,**可通过 `::` 表示==全局命名空间的作用域解析==**,例如 `::MyFunc()` 表示一个在全局命名空间中的函数。 ```cpp // 全局命名空间中的函数 void globalFunction() { // ... } namespace MyNamespace { // MyNamespace 中的函数 void myFunction() { // ... } } int main() { globalFunction(); // 调用全局命名空间中的函数 MyNamespace::myFunction(); // 调用 MyNamespace 中的函数 } ``` > [!note] 函数体内声明的 **==局部变量==** 不属于任何命名空间,局部变量在函数体内可以覆盖以下几种同名变量: > > - 全局命名空间下的同名变量,包括通过 using 声明或 `using namespace` 编译指令从某个命名空间引入到全局命名空间中的变量; > > - 在函数体内部通过 `using namespace` 编译指令从某个命名空间引入到局部声明域的变量。 <br><br> # 内联命名空间 C++11 引入了 “**内联命名空间**”,通过 `inline namespace` 声明,**关键字 `inline` 必须出现在==命名空间第一次定义==的地方**。 内联命名空间中的名字 "**可以被外层命名空间==直接使用==**",而**不需要作用域运算符`::`**。 #### 使用场景 内联命名空间的主要用途是 "**==版本管理==**" 的场景,通过**分隔命名空间**来管理**不同版本的 API**,通过**对其中某一个版本对应的命名空间声明为 `inline`**,来**简化对该版本中 API 的访问**。 使用示例: ```cpp // 某个库所用的命名空间 namespace MyLibrary { namespace V1 { // V1版本中的API void print() { cout << "MyLib v1" << endl; } } namespace V2 { // V2版本中的API void print() { cout << "MyLib v2" << endl; } } inline namespace V3 { // V3 void print() { cout << "MyLib v2" << endl; } } } int main() { // 默认访问inline命名空间(V3)中的版本. MyLibrary::print(); // 也可以明确指定访问旧版本: MyLibrary::V1::print(); MyLibrary::V2::print(); } ``` <br><br> # 匿名命名空间 匿名命名空间中的所有名称具有 "**==内部链接性==**"[^2],只能在**其定义所在的翻译单元**中访问,但**可以在该翻译单元中==匿名命名空间外部直接访问==**。 > [!important] 匿名命名空间的主要作用是 "**==替代 `static` 关键字==**",限制全局作用域下的实体名称为 "**==内部链接性==**"(如全局变量、函数、类名等) ```cpp // 匿名命名空间中的名称, 可以在其命名空间外部、而所属当前翻译单元内部被访问. namespace { int internal_var = 10; void internal_func() { cout << "This is an internal function." << endl; } } void test() { // 可以访问匿名命名空间中的内容 internal_func(); cout << "Internal varaible: " << internal_var << endl; } int main() { test(); // 可以访问匿名命名空间中的内容 std::cout << internal_var << std::endl; internal_func(); } ``` <br><br> # 命名空间别名 可以为命名空间声明 "**==别名==**",声明方式为:`namespace 别名 = 命名空间名字;` ,该别名只在 "**==其声明所在的作用域==**" 中有效: - 当在全局作用域下声明 "**别名**" 时,该别名仅在其所属 "**翻译单元**" 中有效; - 当在块作用域下声明 "**别名**" 时,该别名仅在 "**该块作用域**" 中有效。 > [!NOTE] 可以为一个命名空间声明 "**多个别名**",多个别名可以共存。 ```cpp namespace Outer { namespace Inner { void sayHello() { cout << "Hello from Inner namespace!" << endl; } } } namespace Alias = Outer::Inner; // 定义命名空间别名 void test() { Alias::sayHello(); } int main() { { namespace BBB = Outer::Inner; // 该别名仅在此块作用域中有效 BBB::sayHello(); Alias::sayHello(); // 多个命名空间别名可以共存. } } ``` <br><br><br> # "using 声明" 与 "using 指示" - **using 声明**(using declaration):**一条语句只引入命名空间中的==一个名称==**。 - `using xx::y;`,引入命名空间 `xx` 下的名称 `y` 到 **当前作用域** 中。 - 用于函数时,将引入**该函数名的所有重载版本**。 - **using 指示**(using directive):**引入指定命名空间中的==所有名称==** - `usinge namespace xxx;` ### 区别差异 差异在于两方面: 1. **可使用 "using 声明" o "using 指示" 的位置**; 2. **二者的作用效果差异** ##### (1)使用位置 | | 命名空间作用域 | 局部作用域 | 类作用域中 | | -------- | ------- | ------ | ----------------------------------------- | | using 声明 | ✔️ | ✔️<br> | ✔️(只能用以声明 "**==基类==**" 成员,不能声明其他类的成员)<br> | | using 指示 | ✔️<br> | ✔️<br> | ❌ | ##### (2)作用效果 - **using 声明** 是 **直接向当前作用域内==引入名称==**(即**声明已占用**)。 - 因此,如果**在"当前作用域"下**存在命名冲突时,会报错 "**重复声明错误**"。 - 错误信息:*"Declaration conflicts with target of using declaration already in scope*" - **using 指示** 本质上是为整个命名空间下的所有名称 **==提供一个新的编译时名称查找途径==**。 - 因此,当存在命名冲突时不会立即报错,而只在 **实际访问名称** 时编译器查找**发现多个同名实体**,才会触发 "**名称解析混淆**" 的二义性报错。 - 错误信息: "*Reference to ... is ambiguous*" > [!example] > > 在函数的局部作用域中,**==局部变量会遮蔽==**(或隐藏)**全局** 或 **命名空间作用域** 中的同名实体。因此: > > - 对于 `using namespace xxx;`:在 **全局或函数内** 由此引入的名称**即使与局部变量同名**,也不会导致名称解析混淆的报错。 > - 对于 `using xxx; ` > - 在**全局作用域下**引入的名称与**局部变量同名**,不会报错; > - 在**函数内引入**的名称与**局部变量同名**,则会导致 "**重复声明错误**"。 > [!summary] 总结 > > > `using` 声明或 `using` 指示**并不创建或声明新的实体**,而只是 **为编译器在当前作用域下提供了查找名称的额外路径**。其中: > > - `using` 声明直接向当前作用域引入特定名称,相当于已占用该名称; > - `using` 指示只在实际访问名称才去查找对应实体。 示例一: ```cpp namespace A{ int a = 1; int b = 2; } namespace B{ int b = 22; } namespace C{ int c = 3; } using namespace A; using namespace B; using C::c; int a = 11; int main() { // cout << a << endl; // 报错, 名称解析混淆, "::a" or "A::a" ? cout << ::a << endl; // 全局变量a==11 // cout << b << endl; // 报错, 名称解析混淆, "A::b" or "B::b"? // cout << ::b << endl; // 报错, 名称解析混淆, "A::b" or "B::b"? int b = 222; // 局部变量将隐藏全局或命名空间作用域中的同名实体. cout << b << endl; // 局部变量b==222 cout << A::b << endl; cout << B::b << endl; int c = 333; cout << c << endl; // 局部变量c==333 cout << ::c << endl; // C::c==99 } ``` 示例二: ```cpp namespace MySpace{ int var = 1; void Func() { std::cout<< "invoke func in myspace" << std::endl; } class MyClass { public: MyClass() { std::cout<< "bbc in myspace" << std::endl; } }; } // 使用`using namespace`编译指令向当前作用域中引入命名空间中的所有标识符 using namespace MySpace; int var = 11; void Func() { std::cout<< "invoke func in global namespace" << std::endl; } class MyClass { public: MyClass() { std::cout<< "bbc in global namespace" << std::endl; } }; int main() { // using MySpace2::var; // 此句报错: Declaration conflicts with target of using declaration already in scope int var = 111; std::cout << var << std::endl; std::cout << ::var << std::endl; std::cout << MySpace::var << std::endl; // MyClass obj; // 此句报错: Reference to 'MyClass' is ambiguous MySpace::MyClass obj1; ::MyClass obj2; // Func(); // 此句报错: Call to 'Func' is ambiguous ::Func(); MySpace::Func(); } ``` <br><br> # 涉及命名空间和命名冲突的编译器报错 涉及到命名空间和命名冲突时,两个常见的错误是: - "**==名称解析的模糊性错误==**" (Ambiguous Name Resolution) - "**==重复声明错误==**" (Redeclaration Error) 这两个错误**本质不同**,由不同的情境触发。 ### **名称解析的模糊性错误** 这种错误发生在**编译器找到多个同名实体**,无法确定应该使用哪一个时。 这通常是由 `using` 指示或 `using` 声明引起的,导致在**一个共同的作用域中,存在多个有效的命名实体**可供选择。 如下代码所示: ```cpp namespace A { void foo() { } } namespace B { void foo() { } } using namespace A; using namespace B; void foo() { } // 未报错 int main() { // 当尝试调用 func() 时,有多个有效选择,编译器无法确定应该选择哪一个,因此报告模糊性错误。 foo(); // Ambiguous: A::foo or B::foo or ::foo? } ``` <br> ### **重复声明错误** 这种错误发生在同一个作用域中**多次声明了同一个名称**。 这并不是因为有多个有效的选择,而是**因为在同一个作用域内同一个名称==被重复进行声明==**,从而导致冲突。 如下所示: ```cpp // using A::var; 将 A::var 引入到全局命名空间。 // 接着,`int var = 12;` 在同一个全局命名空间中又声明了一个新的 var // 这导致了冲突,因为现在有两个不同的、不兼容的声明都引用了同一个名称 var。 namespace A { int var = 5; } using A::var; int var = 5; // 报错, 重复声明错误 ``` <br> ### 示例分析 ```cpp //--------------------------------file1.cpp namespace A { int a; void foo() { } } namespace B { int a; void foo() { } } using namespace A; using namespace B; void foo(){ } // 未报错 int a; // 未报错 int main() { // foo(); // 报错, Call to 'foo' is ambiguous. A::foo or B::foo? // a = 10; // 报错, Reference to 'a' is ambiguous. } //--------------------------------file2.cpp namespace A { int a; void foo() { } } namespace B { int a; void foo() { } } using A::foo; using B::foo; // 未报错 //void foo(){ } // 报错, 重复声明错误 using A::a; //using B::a; // 报错, 重复声明错误 //int a; // 报错, 重复声明错误 int main() { // foo(); // 报错, Ambiguous: A::foo or B::foo? } ``` <br><br><br> ##### 问题一:示例一与示例二相比,为什么使用 `using` 编译指令引入同名函数 foo 与同名变量 a 后,全局命名空间下声明/定义的同名函数 foo 和同名变量 a 均未报错重复声明,而在示例二中使用 `using` 声明引入却立即报错? 答:`using` 声明和 `using` 编译指令本身的行为不同。 - `using` 声明 **直接地** 将 **特定名称** 引入当前作用域(即声明已占用),这种声明方式告诉编译器**在当前作用域中对该名称的引用**指向命名空间下的名称。 因此,在示例二中在全局作用域下再次定义函数 `foo` 和变量 `a` 时,**触发了 "重复声明"错误**。 - `using namespace` 本质上是为整个命名空间中的所有名称**提供查找路径**,所以即使存在命名冲突也不会触发重复声明的错误,直到**实际访问名称实体**时编译器查找**发现多个同名实体**,才会触发 "**名称解析混淆**" 的报错。 ##### 问题二: 示例二中,为什么 `using A::foo; using B::foo;` 引入同名函数的这两句没有报错,而 `using A::a; using B::a;` 引入同名变量的这两句却报错了? 答: - C++中允许函数重载,因此**引入两个同名但参数列表不同的函数是可行**的,尽管 `A::foo` 和 `B::foo` 的参数列表完全相同,但 `using` 声明并没有明确地判断两个函数的具体参数列表的情况,因此只有在**实际调用函数** 而根据参数列表来确定是哪一个函数时,才会**触发 "名称解析混淆"** 的报错。 - 而对于**同名变量**,变量名不能被重载,编译器会立即检测到命名冲突,并**报告 "重复声明错误"**。 <br><br> # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later # ♾️参考资料 # Footnotes [^1]: 《C++ Primer》P699 [^2]: 《C++ Primer》P701