%%
# 纲要
> 主干纲要、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