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

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

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;
}

運行結果如下:

Oh Shit!-圖片走丟了-打個廣告-歡迎來部落格園關注「木三百川」

5 總結

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

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

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

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