2009-06-23 11 views
19

Die meisten C++ Programmierer wie ich haben folgende Fehler an einem gewissen Punkt gemacht:Gibt es irgendwelche Verwendung für lokale Funktionsdeklarationen?

class C { /*...*/ }; 

int main() { 
    C c();  // declares a function c taking no arguments returning a C, 
      // not, as intended by most, an object c of type C initialized 
      // using the default constructor. 
    c.foo(); // compiler complains here. 

    //... 
} 

Jetzt, während der Fehler ist ziemlich offensichtlich, wenn Sie es wissen, ich habe mich gefragt, ob es für diese Art von lokaler Funktionsdeklaration jede vernünftige Nutzung ist außer dass Sie es tun können - vor allem, da es keine Möglichkeit gibt eine solche lokale Funktion im selben Block zu definieren; Sie müssen es anderswo definieren.

Ich denke, dass lokale Java-Klassen eine ziemlich nette Funktion sind, die ich oft verwende, vor allem die anonyme Sortierung. Sogar lokale C++ - Klassen (die Inline-definierte Member-Funktionen haben können) haben einige Verwendung. Aber diese lokale Funktionserklärung ohne Definitionssache erscheint mir sehr peinlich. Ist es nur ein C-Vermächtnis oder gibt es einen tieferen Anwendungsfall, den ich nicht kenne?

bearbeiten für die Nichtgläubigen: C c() ist nicht eine Funktion Zeigerdeklaration.

Dieses Programm

int main() 
{ 
    void g(); 
    cout << "Hello "; 
    g(); 
    return 0; 
} 

void g() 
{ 
    cout << "world." << endl; 
} 

Ausgänge Hello world. Dieses Programm

void fun() 
{ 
    cout << "world." << endl; 
} 

int main() 
{ 
    void g(); 
    g = fun; 
    cout << "Hello "; 
    g(); 
    return 0; 
} 

nicht kompiliert. gcc klagt:

error: cannot convert 'void()()' to 'void()()' in assignment

comeau:

error: expression must be a modifiable lvalue
+0

Beachten Sie, dass die GCC-Fehlermeldung falsch ist (bekannter Fehler). Es sollte "error: convert 'void()' in 'void()' in assignment" nicht konvertiert werden, aber es wird die Type-ID falsch abgefragt. (anstelle von "void()()", was eine Funktion wäre, die eine Funktion zurückgibt (illegaler Typ), sollte sie "void()" sagen –

+0

verwandte Frage: http://stackoverflow.com/questions/928992/nested -funktionen-sind-nicht-erlaubt-aber-warum-geschachtelt-funktion-prototypen-sind erlaubt –

+0

Ich bin mir nicht sicher, ob die gcc-Fehlermeldung ein Fehler ist - Sie können die Funktion sogar mit einem zusätzlichen Klammerpaar deklarieren ' void (g)() ', die Sie brauchen, wenn Sie mit Funktionszeigern umgehen:' void * g() 'ist eine Funktion, die ein' void * 'zurückgibt, während' void (* g)() 'ein Zeiger auf eine Funktion ist vom Typ, nun, 'void()()', dh keine Argumente zu nehmen und nichts zurückzugeben. – Tobias

Antwort

2

Ich wollte lokale Funktionsdeklarationen in C, wenn ich sie als Argumente an eine andere Funktion übergeben wollte. Ich mache das die ganze Zeit in anderen Sprachen. Der Grund ist, die Implementierung von Datenstrukturen zu kapseln.

z. Ich definiere eine Datenstruktur, z.B. ein Baum oder ein Graph, und ich möchte die Details seiner internen Implementierung, z. weil ich es vielleicht ändern oder ändern möchte. So offenbare ich Accessor- und Mutator-Funktionen für seine Elemente und eine Traversierungsfunktion, um über die Elemente zu iterieren. Die Traversierungsfunktion hat als Argument eine Funktion, die auf ein Element wirkt; Die Aufgabe der Traversierungsfunktion besteht darin, die Argumentfunktion für jedes Element auszuführen und möglicherweise die Ergebnisse auf irgendeine Weise zu aggregieren. Wenn ich nun die Traversalfunktion aufrufe, ist die Argumentfunktion normalerweise eine spezialisierte Operation, die vom lokalen Zustand abhängt und daher als lokale Funktion definiert werden sollte. Die Funktion mit den Variablen, die den lokalen Zustand enthalten, nach außen zu schieben, um global zu sein, oder in eine innere Klasse, die speziell dafür geschaffen wurde, sie zu halten, ist hässlich. Aber das ist ein Problem, das ich mit C und Java habe, nicht mit C++.

+0

OK, Sie sagen also, es ist ein C-Erbe, das seinen Zweck in C++ verloren hat? – Tobias

+0

Nicht genau. Ich bin nicht wirklich genug erfahren in C++ - mit oder ohne Vorlagen - um sagen zu können, wie ich diese Situation normalerweise angehen würde. Iteratoren sind in C++ üblich, aber über Traversalmethoden, die Funktionen als Argumente annehmen, bin ich nicht so sicher. – reinierpost

+0

Update: siehe http://stackoverflow.com/questions/2116128/easier-way-to-do-callbacks-for-vectors-or-aye-etwas-el-se-in-the-stl-c – reinierpost

2

Es ist eine Vorwärtsdeklaration Prototyp. Wenn Sie viele lokale Funktionen oder lokale Funktionen haben, die sich gegenseitig entgegen der Definitionsreihenfolge aufrufen, benötigen Sie sie möglicherweise.

+0

Ich verstehe Ihren Punkt hier nicht: In Ihrem Anwendungsfall würde ich nur alle Funktionen am Anfang der Quelldatei (oder sogar in einem privaten Header) deklarieren und dann glücklich umsetzen. Warum sollte ich jemals eine Funktion in einer anderen Funktion _declare_ wollen? – Tobias

10

Die einzige Anwendung ich denken kann, ist der Umfang der Funktionsdeklarationen zu reduzieren:

int main() 
{ 
    void doSomething(); 
    doSomething(); 
    return 0; 
} 

void otherFunc() 
{ 
    doSomething(); // ERROR, doSomething() not in scope 
} 

void doSomething() 
{ 
    ... 
} 

Natürlich gibt es viel bessere Lösungen für diese. Wenn Sie eine Funktion ausblenden möchten, sollten Sie Ihren Code wirklich umstrukturieren, indem Sie Funktionen in separate Module verschieben, so dass sich die Funktionen, die die Funktion, die Sie ausblenden möchten, aufrufen müssen, alle im selben Modul befinden. Dann können Sie diesen lokalen Funktionsbaustein erstellen, indem Sie ihn als statisch deklarieren (die C-Methode) oder indem Sie ihn in einen anonymen Namespace (die C++ - Methode) einfügen.

2

Die einzige vernünftige Verwendung dafür sehe ich darin, nur einer Funktion in einer Kompilierungseinheit zu erlauben, von einer Funktion zu wissen, die in einer anderen Kompilierungseinheit definiert ist. Ich denke, das wäre ein einigermaßen vernünftiger Nutzen, aber das ist der einzige, den ich mir vorstellen kann, und ich denke, das ist Overkill.

+0

Aber warum würden Sie eine Funktion innerhalb der Definition eines anderen (im Gegensatz zu außerhalb der Funktionsdefinition oder in einem Header) deklarieren? – Tobias

+1

Wie ich schon sagte, damit nur diese Funktion es sehen kann. –

-1

Wenn Sie zwischen dem Deklarieren einer Funktion, die keine Parameter akzeptiert, und dem Instanziieren einer Klasse mit dem Standarddestruktor unterscheiden möchten, überspringen Sie die Klammern.

class C 
{ 
    public: 
    void foo() {} 
}; 


int main() 
{ 
    // declare function d, taking no arguments, returning fresh C 
    C d(); 

    // instantiate class (leave parenthesis out) 
    C c; 
    c.foo(); 

    // call declared function 
    C goo = d(); 

    return 0; 
} 

C d() 
{ 
    C c; 
    // .. 
    return c; 
} 
+1

Meine Frage ist nicht, wie ein Objekt zu instantiieren, meine Frage ist, warum würden Sie jemals die Funktion erklären, wie Sie es getan haben (im Gegensatz zu deklarieren es außerhalb der Haupt)? – Tobias

0

Ich mache es manchmal aus dem gleichen Grunde wir ermutigt werden, Variablen zu deklarieren direkt vor dem ersten Gebrauch (und nicht früher), nämlich die Lesbarkeit zu verbessern. (Ja, ich weiß, dass es für Variablen wichtiger ist, weil es Sie von der Notwendigkeit befreit, zu prüfen, ob die Variable vor dem verwendet wird, von dem Sie denken, dass es zum ersten Mal verwendet wird).Wenn der Prototyp (insbesondere wenn er mehr involviert ist als nur c()) dem Funktionsaufruf nahe kommt, verbessert dies die Lesbarkeit. Die Tatsache, dass der Sonderfall C c() für Menschen irreführend ist, ist bedauerlich, aber die allgemeine Idee, Funktionen lokal zu deklarieren, hat Vorteile.

Dies gilt natürlich auch für Funktionsnamen, die bereits im Gültigkeitsbereich sind. Warum nicht jede Funktion vor jeder Verwendung neu deklarieren? Meine Antwort darauf: Mache alle Dinge in Maßen. Zu viel Unordnung beeinträchtigt die Lesbarkeit (sowie die Wartbarkeit - sollte sich die Signatur der Funktion jemals ändern). Obwohl ich keine Regel daraus machen würde, ist es manchmal nützlich, Funktionen lokal zu deklarieren.

+0

Haben Sie es jemals getan? Kennst du jemanden, der das jemals getan hat? Ich tue es nicht, und es wäre großartig, wenn Sie sich mit einem Beispiel von _real_world verbinden könnten. Das wäre wirklich interessant. – Tobias

+0

Ich habe eine vage Erinnerung daran, es tatsächlich ein- oder zweimal zu tun, aber ich erinnere mich nicht an eine bestimmte Instanz. Die Sache ist, dass die meisten Funktionen, die ich nenne, Klassenmethoden sind, also ist es ziemlich selten. – Ari

1

Wie im 3. Snippet von this answer erwähnt, kann es bei der Bereichsschattenbildung helfen.

#include <stdio.h> 

void c(); // Prototype of a function named ``c'' 

int main() { 

    c(); // call the function 

    { // additional scoppe needed for C, not for C++ 
     int c = 0; // shadow the c function with a c integer variable 
     c++; 
     { 
      void c(); // hide the c integer variable back with the c function 
      c(); 
     } 
     ++c; 
    } //! 

    return 0; 
} 

void c() { 
    printf("Called\n"); 
} 

Ich glaube nicht, diese Nutzung wahrscheinlich ist oft nützlich sein, und es gibt Chancen, dass es nicht in jedem gut konzipiertes Programm entstehen.

Ich denke immer noch, dass dies der wahrscheinlichste Grund für dieses Feature ist, Funktionen in letzter Zeit zu definieren, da für Variablen einfach nicht so richtig klingt.

Verwandte Themen