2009-05-30 3 views
7

Ich war reading die verknüpfte Frage, die mich dazu bringt, diese Frage zu stellen.Verschachtelte Funktionen sind nicht erlaubt, aber warum geschachtelte Funktionsprototypen erlaubt sind? [C++]

Betrachten Sie den folgenden Code

int main() 
{ 
    string SomeString(); 
} 

Alle sagen, Compiler dies als Funktionsprototyp nimmt und nicht als String Objekt. Betrachten Sie nun den folgenden Code.

Compiler sagte, das ist ungültig, da ich denke, geschachtelte Funktionsdefinition ist nicht erlaubt. Wenn es nicht erlaubt ist, warum geschachtelte Funktionsprototypen erlaubt sind? Es gibt keinen Vorteil, anstatt Verwirrung zu stiften (oder fehlen mir hier einige gültige Punkte?).

Ich fand heraus, das folgende ist gültig.

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

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

Dies ist auch verwirrend. Ich erwartete die Funktion SomeFun() wird einen Bereich nur in Haupt haben. Aber ich habe mich getäuscht. Warum Compiler ermöglicht, Code wie oben zu kompilieren? Gibt es Situationen in Echtzeit, in denen Code wie oben sinnvoll ist?

Irgendwelche Gedanken?

+0

+1 nur auf die gleiche Abfrage, und die Antworten unten haben alle Informationen und mehr. – slashmais

Antwort

8

Ihr Prototyp ist nur 'Forward Declaration'. Bitte schau dir den Wikipedia Artikel an.

Grundsätzlich sagt es dem Compiler "nicht alarmiert werden, wenn das Label 'SomeFun' auf diese Weise verwendet wird". Aber Ihr Linker ist verantwortlich dafür, den richtigen Funktionskörper zu finden.

Sie können tatsächlich einen falschen Prototyp deklarieren, z. 'Char' SomeFun() 'und benutze es überall auf deinem Main. Sie erhalten nur einen Fehler, wenn Ihr Linker versucht, den Körper Ihrer falschen Funktion zu finden. Aber dein Compiler wird damit cool sein.

Es gibt viele Vorteile. Sie müssen sich daran erinnern, dass sich der Funktionskörper nicht immer in der gleichen Quellcodedatei befindet. Es kann in einer verbundenen Bibliothek sein. Außerdem kann diese verbundene Bibliothek eine spezifische 'Verbindungssignatur' haben. Bedingte Bedingungen definieren, können Sie sogar die korrekte Verbindungsunterschrift zur Erstellungszeit unter Verwendung Ihrer begrenzten Prototypen auswählen. Obwohl die meisten Leute Funktionszeiger für verwenden würden das stattdessen.

Hoffe, das hilft.

5

Dies ist eine Konvention von C - wie viele - die C++ übernommen hat.

Die Fähigkeit, eine Funktion in einer anderen Funktion in C zu deklarieren, ist eine Entscheidung, die die meisten Programmierer wahrscheinlich für bedauerlich und unnötig halten. Vor allem bei modernen OOP Design, bei dem Funktionsdefinitionen sind vergleichsweise kleiner, als sie in C sind

Wenn Sie Funktionen haben möchten, die nur im Rahmen einer anderen Funktion existieren, sind zwei Optionen boost::lambda und C++1x lambda.

3

Funktionsprototypen sind Hinweise für den Compiler.Sie zeigen an, dass die Funktionen woanders implementiert sind, wenn nicht schon entdeckt. Nichts mehr.

3

Wenn Sie einen Prototyp so deklarieren, wie Sie gerade arbeiten, sagen Sie dem Compiler im Grunde, auf den Linker zu warten, um ihn zu lösen. Je nachdem, wo Sie den Prototyp schreiben, gelten die Scoping-Regeln. Es gibt nichts technisch falsch Schreiben des Prototyps innerhalb Ihrer Main() -Funktion (obwohl IMHO ein bisschen unordentlicher), es bedeutet nur, dass die Funktion nur lokal in der Main() bekannt ist. Wenn Sie den Prototyp am Anfang Ihrer Quelldatei (oder häufiger in einer Header-Datei) deklariert hätten, wäre der Prototyp/die Funktion in der gesamten Quelle bekannt.

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

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

Nur als Nebenbemerkung, C++ 03 hat einen Umweg über lokale Funktionen zu definieren. Es erfordert missbrauchen die lokale Klasse Feature:

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

Awesome Missbrauch der Sprache. – Joshua

+0

Warum ist das Missbrauch? (Ich bin echt neugierig übrigens) – Samaursa

5

Wie, warum Ihre Erklärung

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

ist besser als diese

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

Es ist im Allgemeinen gut, wenn man Erklärungen als lokale halten so möglich, dass so wenig Namenskonflikte wie möglich entstehen. Ich sage, es ist fraglich, ob eine Funktion lokal (auf diese Weise) zu deklarieren ist wirklich Glück, wie ich denke, es ist immer noch besser zu normalen gehören seinen Header und dann gehen die "üblichen" Weg, die auch weniger verwirrend für die Menschen nicht darüber wissen . Manchmal ist es auch nützlich, um einen schattierten Funktion

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

Natürlich in C++ arbeiten wir Funktion aufrufen könnte gits_namespace::g() mit - aber in den alten Tagen von C, die nicht möglich gewesen wäre, und das Ding erlaubt dem Programmierer, immer noch auf die Funktion zuzugreifen. Beachten Sie auch, dass syntaktisch nicht dasselbe ist, aber das Folgende deklariert semantisch auch eine Funktion innerhalb eines lokalen Bereichs, die eigentlich auf einen anderen Bereich abzielt.

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

Als Randbemerkung, gibt es mehr Situationen, dass der Zielbereich einer Erklärung, wo nicht den Umfang ist, wo diese Erklärung angezeigt wird. Im Allgemeinen Sie das Unternehmen erklären, Mitglied des Umfangs wird in dem die Deklaration erscheint. Aber das ist nicht immer der Fall. Betrachten wir zum Beispiel Freund Erklärungen, wo das Ding

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

Auch wenn der Funktionsdeklaration geschieht (und Definition!) Von f im Rahmen von X passiert ist, das Unternehmen (die Funktion selbst) wurde ein Mitglied des einschließenden Namespace .

+0

Schöne Erklärung. Vielen Dank litb –

+1

+1 Das letzte 'Freund'-Ding ist seltsam - kann einfach nicht Freunde vertrauen;) – slashmais

+0

Ich bekomme nicht den 'Freund' Trick. Ist macht 'X :: f()' zum einschließenden Namespace? – ofavre