C++ using 編譯指令與名稱衝突

2022-11-21 12:01:31

using 編譯指令:它由名稱空間名和它前面的關鍵字 using namespace 組成,它使名稱空間中的所有名稱都可用,而不需要使用作用域解析運運算元。在全域性宣告區域中使用 using 編譯指令,將使該名稱空間的名稱全域性可用;在函數或程式碼塊中使用 using 編譯指令,將使其中的名稱在該函數或程式碼塊中可用。當包含 using 宣告的最小宣告區域中已經宣告了和名稱空間中相同的名稱時,若仍使用 using 宣告匯入該名稱空間的同名名稱,則這兩個名稱將會發生衝突,編譯器會報錯。using 宣告不同的是,using 編譯指令會進行名稱解析,在一些時候名稱空間的變數會被同區域宣告的同名變數隱藏,不會出現名稱衝突的報錯。但在另一些情況下,使用 using 編譯指令仍會出現名稱衝突的報錯,下面對此進行總結,測試所用的環境為 Microsoft Visual Studio 2019 以及 QT 5.9.2 MinGW 32bit

1 using 編譯指令與同名全域性變數

結論:若僅存在同名全域性變數,不存在同名區域性變數,使用 using 編譯指令後,在作用域的重合區域使用變數時一定會引發名稱衝突。除非在同名全域性變數宣告前的程式碼塊中使用,但這時是因為同名變數的作用域不重合,而非 using 編譯指令名稱解析的功勞。

1.1 在同名全域性變數宣告前使用

若在同名全域性變數宣告前的程式碼塊中使用,由於作用域不重合,一定不會引發名稱衝突,因此只需測試在同名全域性變數宣告前的全域性區中使用 using 編譯指令的效果。測試程式如下:(出現名稱衝突報錯)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//在同名全域性變數宣告前使用
using namespace Jack;

//在全域性名稱空間中定義變數
double pail = 2;

//測試
int main()
{
    using namespace std;
    
    //使用
    cout << pail << endl;
    cout << ::pail << endl;
    cout << Jack::pail << endl;
    
    return 0;
}

執行結果如下:

1.2 在同名全域性變數宣告後的全域性區中使用

測試程式如下:(出現名稱衝突報錯)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//在全域性名稱空間中定義變數
double pail = 2;

//在同名全域性變數宣告後使用
using namespace Jack;

//測試
int main()
{
    using namespace std;
    
    //使用
    cout << pail << endl;
    cout << ::pail << endl;
    cout << Jack::pail << endl;
    
    return 0;
}

執行結果如下:

1.3 在同名全域性變數宣告後的程式碼塊中使用

測試程式如下:(出現名稱衝突報錯)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//同名全域性變數宣告
double pail = 2;

//測試
int main()
{
    using namespace std;
   
    //使用
    using namespace Jack;
    cout << pail << endl;
    cout << ::pail << endl;
    cout << Jack::pail << endl;
    
    return 0;
}

執行結果如下:

2 using 編譯指令與同名區域性變數

結論:若僅存在同名區域性變數,不存在同名全域性變數,使用 using 編譯指令將會進行名稱解析,不會引發名稱衝突,但在程式碼塊中,同名區域性變數將隱藏名稱空間中的變數。

2.1 在同名區域性變數宣告前的全域性區中使用

測試程式如下:(執行成功)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//在同名區域性變數宣告前的全域性區中使用
using namespace Jack;

//測試
int main()
{
    using namespace std;
    
    //同名區域性變數
    double pail = 2;
    
    //使用
    cout << pail << endl;       //結果為2
    cout << ::pail << endl;     //結果為1
    cout << Jack::pail << endl; //結果為1
    
    return 0;
}

執行結果如下:

2.2 在同名區域性變數宣告前的程式碼塊中使用

測試程式如下:(執行成功)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//測試
int main()
{
    using namespace std;
    
    //在同名區域性變數宣告前的程式碼塊中使用
    using namespace Jack;
    
    //同名區域性變數
    double pail = 2;
    
    //使用
    cout << pail << endl;       //結果為2
    cout << Jack::pail << endl; //結果為1
    
    return 0;
}

執行結果如下:

2.3 在同名區域性變數宣告後使用

若在同名區域性變數宣告後的全域性區中使用,由於作用域不重合,一定不會引發名稱衝突,因此只需測試在同名區域性變數宣告後的程式碼塊中使用 using 編譯指令的效果。測試程式如下:(執行成功)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//測試
int main()
{
    using namespace std;
    
    //同名區域性變數
    double pail = 2;
    
    //在同名區域性變數宣告後的程式碼塊中使用
    using namespace Jack;
    
    //使用
    cout << pail << endl;       //結果為2
    cout << Jack::pail << endl; //結果為1
    
    return 0;
}

執行結果如下:

3 不同名稱空間中的同名變數

結論:若不同名稱空間中存在同名變數,不存在同名全域性變數以及同名區域性變數,使用 using 編譯指令後,在作用域的重合區域使用變數時一定會引發名稱衝突。

3.1 using 編譯指令位置都在全域性區中

測試程式如下:(出現名稱衝突報錯)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}
namespace Rose {
    double pail = 2;
}

//都在全域性區中
using namespace Jack;
using namespace Rose;

//測試
int main()
{
    using namespace std;
    
    //使用
    cout << pail << endl;
    
    return 0;
}

執行結果如下:

3.2 using 編譯指令位置都在程式碼塊中

測試程式如下:(出現名稱衝突報錯)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}
namespace Rose {
    double pail = 2;
}

//測試
int main()
{
    using namespace std;
    
    //都在程式碼塊中
    using namespace Jack;
    using namespace Rose;
    
    //使用
    cout << pail << endl;
    
    return 0;
}

執行結果如下:

3.3 using 編譯指令位置不同區

測試程式如下:(出現名稱衝突報錯)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}
namespace Rose {
    double pail = 2;
}

//Jack位於全域性區中
using namespace Jack;

//測試
int main()
{
    using namespace std;
    
    //Rose位於程式碼塊中
    using namespace Rose;
    
    //使用
    cout << pail << endl;
    
    return 0;
}

執行結果如下:

4 多個同名變數共存

結論:若名稱空間中的變數、同名全域性變數、同名區域性區域性變數三者同時存在,using 編譯指令的使用位置不會影響名稱解析的結果,且不會引發名稱衝突,這正是 using 編譯指令進行名稱解析的效果。

4.1 在同名全域性變數宣告前的全域性區中使用

測試程式如下:(執行成功)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//在同名全域性變數宣告前的全域性區中使用
using namespace Jack;

//同名全域性變數
double pail = 2;

//測試
int main()
{
    using namespace std;
    
    //同名區域性變數
    double pail = 3;
    
    //使用
    cout << pail << endl;       //結果為3
    cout << ::pail << endl;     //結果為2
    cout << Jack::pail << endl; //結果為1
    
    return 0;
}

執行結果如下:

4.2 在同名全域性變數宣告後的全域性區中使用

測試程式如下:(執行成功)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//同名全域性變數
double pail = 2;

//在同名全域性變數宣告後的全域性區中使用
using namespace Jack;

//測試
int main()
{
    using namespace std;
    
    //同名區域性變數
    double pail = 3;
    
    //使用
    cout << pail << endl;       //結果為3
    cout << ::pail << endl;     //結果為2
    cout << Jack::pail << endl; //結果為1
    
    return 0;
}

執行結果如下:

4.3 在同名區域性變數宣告前的程式碼塊中使用

測試程式如下:(執行成功)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//同名全域性變數
double pail = 2;

//測試
int main()
{
    using namespace std;
    
    //在同名區域性變數宣告前的程式碼塊中使用
    using namespace Jack;
    
    //同名區域性變數
    double pail = 3;
    
    //使用
    cout << pail << endl;       //結果為3
    cout << ::pail << endl;     //結果為2
    cout << Jack::pail << endl; //結果為1
    
    return 0;
}

執行結果如下:

4.4 在同名區域性變數宣告後的程式碼塊中使用

測試程式如下:(執行成功)

#include <iostream>

//自定義名稱空間
namespace Jack {
    double pail = 1;
}

//同名全域性變數
double pail = 2;

//測試
int main()
{
    using namespace std;
    
    //同名區域性變數
    double pail = 3;
    
    //在同名區域性變數宣告後的程式碼塊中使用
    using namespace Jack;
    
    //使用
    cout << pail << endl;       //結果為3
    cout << ::pail << endl;     //結果為2
    cout << Jack::pail << endl; //結果為1
    
    return 0;
}

執行結果如下:

5 總結

通過上述多個測試,可以得到以下結論:

  • 若僅存在同名全域性變數,不存在同名區域性變數,使用 using 編譯指令後,在作用域的重合區域使用變數時一定會引發名稱衝突。
  • 若僅存在同名區域性變數,不存在同名全域性變數,使用 using 編譯指令將會進行名稱解析,不會引發名稱衝突,但在程式碼塊中,同名區域性變數將隱藏名稱空間中的變數。
  • 若不同名稱空間中存在同名變數,不存在同名全域性變數以及同名區域性變數,使用 using 編譯指令後,在作用域的重合區域使用變數時一定會引發名稱衝突。
  • 若名稱空間中的變數、同名全域性變數、同名區域性區域性變數三者同時存在,using 編譯指令的使用位置不會影響名稱解析的結果,且不會引發名稱衝突,這正是 using 編譯指令進行名稱解析的效果。

Jack 名稱空間中的 pail 變數為例,將使用 using 編譯指令時可能遇到的各種情況列表如下,表中的最後一列是指在作用域的重合區域使用變數時是否會引發名稱衝突。

場景 同名全域性變數 pail 同名區域性變數 pail 另一名稱空間 Rose 的同名變數 pail using 編譯指令是否名稱衝突
1 存在 衝突
2 存在 存在 衝突
3 存在 不衝突
4 存在 存在 不衝突
5 存在 衝突
6 存在 存在 不衝突
7 存在 存在 存在 不衝突
8 不衝突