C++域作用符在函數聲明和定義中的意義?
如下代碼在是vs2015:
void f() {}
class a1
{
public:
void f() {}
void g()
{
void f(); //void ::f();
f();
}
}
這樣的話,很自然調用的是全局的f()。然而使用了注釋里的聲明後,不會報錯,但是調用的是成員函數f。
為何在函數聲明中使用:: 不會出錯,但是應該是無效的聲明(畢竟之後的調用沒有受到其影響)。請問語法分析對函數聲明中的域作用符是如何解析的?另外我發現在如下代碼中:
int f() {return 0;}
int ::f() {return 1;}
int ::f() {return 2;}int main()
{
return f();
}
vs2015不會報錯,甚至將f()的定義認同為return 2的那個,也就是最後一次出現的f()定義。這樣的話就有些詭異難以理解,請高手予以解釋。
當然,這些代碼在gcc中全部會出錯,指明了域作用符這樣用是不對的,應該是符合預期的。關鍵是想問一下關於這一點有什麼特殊之處,導致兩個編譯器對此有明顯的不同?標準中是怎麼解釋的?另外有興趣的話也請大家麻煩看下這個問題,http://www.zhihu.com/question/45766253,希望能從語法分析的層面解釋一下錯誤的原因。
先說第一部分。函數聲明中使用::在語法上沒問題,這裡的聲明有效,引用的是全局作用域中的f。以void ::f();為例,自頂向下的語法展開路徑大致是這樣的,這裡省略了部分為空的展開。
declaration-seq
-&> block-declaration
-&> simple-declaration
-&> decl-specifier-seq init-declarator-list ;
-&> decl-specifier init-declarator-list ;
-&> type-specifier init-declarator-list ;
-&> simple-type-specifier init-declarator-list ;
void -&> init-declarator-list ;
void -&> init-declarator ;
void -&> declarator ;
void -&> noptr-declarator parameters-and-qualifiers ;
void -&> declarator-id parameters-and-qualifiers ;
void -&> id-expression parameters-and-qualifiers ;
void -&> qualified-id parameters-and-qualifiers ;
void -&> nested-name-specifier unqualified-id parameters-and-qualifiers ;
void :: -&> unqualified-id parameters-and-qualifiers ;
void :: f -&> parameters-and-qualifiers ;
void :: f -&> (parameter-declaration-clause) ;
void :: f () ;
::是從nested-name-specifier展開得到的,屬於qualified-id的一部分。
void ::f();這種聲明表示在全局作用域中f這個名字指代一個非成員函數。調用f時g的作用域里並不存在f,所以向上一層,最先找到的是當前類成員函數,類中不存在f這個名字的時候才會去全局作用域找,如果想要直接調用全局的同樣要加上::操作符。
而void f();這種聲明則表示在當前作用域中f這個名字指代一個非成員函數。調用f時最先找到這個聲明,繼續找定義就會找到全局的版本,因為類中的f是成員函數。
關於第二部分,同一文件中重複定義違背了One-Definition Rule(ODR),標準中也沒有其它規定為全局作用域的函數作特殊處理,VC的作法不合標準。其實如果不是全局而是具體指定了命名空間或是某個類的話,VC是可以檢測到重複定義的,也許是全局空間的處理邏輯沒考慮全,我覺得可以提個bug。看了看第二個程序,跟了下彙編,發現前邊兩個f() vs2015都沒有反彙編出來,也肯定打不上斷點!
int a = 3;
int main()
{int a = 4;
std::cout&<&< a&<&a輸出是4,這時候我懷疑是第一個作用域把第二個作用域包含進去了,導致只有第二個函數才有用。同理第二個把第三個包含進去了,但是這樣也不很怪,因為這樣函數輸出是不會很慢的,實際上我寫第三個這樣的程序的時候
#include&
int a() { return 32; }
int ::a() { return 33; }int main()
{ std::cout&<&}int ::a() { return 34; }
程序的響應是非常慢的而且輸出的是34。將近2s.現在比較懷疑的是編譯器把這三個全局函數也重載了。醬Cpp primer 5e 7.4.1
1 函數中不允許定義函數2 第二個默認應該是redefinition 應該是VC為了一些歷史代碼瞎搞出的兼容/bug
推薦閱讀:
※在 64 位平台開發時是否應盡量避免使用指針?
※C++中為什麼派生類中只有基本類型時,delete一個指向派生類的基類指針時卻沒有內存泄漏?
※c++在執行運行時多態時,為什麼需要借用rtti來判斷對象真實類型?
※一個關於C++模板的問題?
※關於c++模板推導失敗,這是編譯器的bug嗎?