2009-05-30 75 views
7

我是reading導致我問這個問題的關聯問題。嵌套函數不被允許,但爲什麼嵌套函數原型被允許? [C++]

考慮下面的代碼

int main() 
{ 
    string SomeString(); 
} 

所有說,編譯器將其視爲函數原型,而不是作爲一個字符串對象。現在考慮下面的代碼。

int main() 
{ 
    string Some() 
    { 
     return ""; 
    } 
} 

編譯器說這是無效的,因爲我猜嵌套函數定義是不允許的。 如果不允許,爲什麼允許嵌套的函數原型?它沒有給予任何好處,而不是讓人混淆(或者我在這裏錯過了一些有效的觀點?)。

我想通以下是有效的。

int main() 
{ 
    string SomeFun(); 
    SomeFun(); 
    return 0; 
} 

string SomeFun() 
{ 
    std::cout << "WOW this is unexpected" << std::endl; 
} 

這也令人困惑。我期待功能SomeFun()將只有在主要有一個範圍。但是我錯了。爲什麼編譯器允許編譯上面的代碼?是否有任何實時情況下,上述代碼有意義?

有什麼想法?

+0

+1只需點擊相同的查詢,&下面的答案包含所有信息和更多信息。 – slashmais 2010-11-04 05:39:14

回答

8

您的原型僅爲'Forward Declaration'。請查看Wikipedia文章。

基本上,它告訴編譯器「如果以這種方式使用標籤'SomeFun',不要驚慌」。但是你的鏈接器是負責找到正確的函數體的東西。

你實際上可以聲明一個僞造的原型,例如, 'char SomeFun()'並將它用在你的主體上。鏈接器嘗試查找虛假函數的主體時,只會出現錯誤。但是你的編譯器會很酷。

有很多好處。你必須記住函數體並不總是在同一個源代碼文件中。它可以位於鏈接庫中。此外,鏈接庫可能具有特定的「鏈接簽名」。使用條件定義,甚至可以在構建時使用範圍化原型選擇正確的鏈接簽名。儘管大多數人會使用函數指針相反。

希望這會有所幫助。

5

這是一個來自C的慣例 - 就像許多人一樣 - 這是C++採用的。

在C中另一個函數中聲明函數的能力是大多數程序員可能認爲令人遺憾和不必要的決定。特別是隨着現代OOP的設計,其中函數定義比他們在C.

如果你想有,只有在另一個函數的範圍存在功能相對較小,有兩個選項boost::lambdaC++1x lambda

3

編譯器的函數原型是提示。他們表示,這些功能是在其他地方實施的,如果不是已經發現的。而已。

3

當你在聲明原型時,你基本上是告訴編譯器等待鏈接器來解決它。根據您編寫原型的範圍規則適用。在你的main()函數中寫入原型沒有任何技術上的錯誤(儘管恕我直言,有點混亂),它只是意味着函數只在main()內部是本地已知的。如果您將原型聲明在源文件的頂部(或者更常見的是在頭文件中),那麼原型/函數將在整個源代碼中是已知的。

string foo() 
{ 
    string ret = someString(); // Error 
    return ret; 
} 

int main(int argc,char**argv) 
{ 
    string someString(); 
    string s = somestring(); // OK 
    ... 
} 
7

正如附註,C++ 03確實有一個定義本地函數的迂迴方式。它要求濫用地方級功能:

int main() 
{ 
    struct Local 
    { 
     static string Some() 
     { 
      return ""; 
     } 
    }; 
    std::cout << Local::Some() << std::endl; 
} 
+2

令人敬畏的語言濫用。 – Joshua 2009-05-30 15:29:02

+0

這是爲什麼'濫用'? (我真的很好奇btw) – Samaursa 2012-02-07 20:33:34

5

至於爲什麼你的

void f() { 
    void g(); g(); 
} 

聲明是一個比這

void g(); 
void f() { 
    g(); 
} 

它總體上是好的更好,如果你把聲明的地方儘可能避免名稱衝突。我認爲在本地聲明一個函數(這種方式)是否真的是真的是是有爭議的,因爲我認爲普通的包含它的標題還是更好,然後採用「通常」的方式,這對於不知道。有時,它也是有用的解決被遮擋的功能

void f() { 
    int g; 
    // oops, ::g is shadowed. But we can work around that 
    { 
     void g(); g(); 
    } 
} 

當然,在C++中,我們可以用its_namespace::g()調用函數g - 但在過去的C,會​​不會有可能的,而且那件事允許程序員仍然可以訪問該函數。還要注意,雖然語法上不同,但從語義上講,以下內容也在本地範圍內聲明瞭一個函數,該函數實際上定位了不同的範圍。

int main() { 
    using std::exit; 
    exit(); 
} 

作爲一個側面說明,有更多的情況一樣,在那裏聲明的目標範圍是如該聲明出現在範圍內。一般來說,你聲明的實體變爲範圍中的一員在該聲明中出現。但情況並非總是如此。例如,考慮朋友的聲明,其中說的事情發生

struct X { friend void f() { std::cout << "WoW"; } }; 
int main() { void f(); f(); } // works! 

即使函數聲明(和定義!)的f發生的X範圍內,在實體(函數本身)成爲封閉命名空間中的一員。