Können wir functionen in functionen haben?

Ich meine etwas wie:

int main() { void a() { // code } a(); return 0; } 

Nein, C ++ unterstützt das nicht.

Edit: Diese Antwort ist alt. In der Zwischenzeit hat C ++ 11 Lambdas, die ein ähnliches Ergebnis erzielen können – siehe Antworten unten.

Das heißt, Sie können lokale classn haben, und sie können functionen haben (nicht static oder static ), also können Sie das etwas erreichen, auch wenn es ein bisschen wie ein Klotz ist:

 int main() // it's int, dammit! { struct X { // struct's as good as class static void a() { } }; X::a(); return 0; } 

Ich würde jedoch die Praxis in Frage stellen. Jeder weiß es (nun, da du es sowieso tust :) ) C ++ unterstützt keine lokalen functionen, daher sind sie es gewohnt, sie nicht zu haben. Sie werden jedoch nicht zu diesem Kludschlamm verwendet. Ich würde eine ganze Weile auf diesen Code verwenden, um sicherzustellen, dass es nur dort ist, um lokale functionen zuzulassen. Nicht gut.

Für alle Zwecke unterstützt C ++ dies über lambdas : 1

 int main() { auto f = []() { return 42; }; std::cout < < "f() = " << f() << std::endl; } 

Hier ist f ein Lambda-Objekt, das in main als lokale function fungiert. Captures können angegeben werden, damit die function auf lokale Objekte zugreifen kann.

Hinter den Kulissen ist f ein functionsobjekt (dh ein Objekt eines Typs, der einen operator() bereitstellt). Der functionsobjekttyp wird vom Compiler basierend auf dem Lambda erstellt.


1 seit C ++ 11

Lokale classn wurden bereits erwähnt, aber hier ist eine Möglichkeit, sie noch mehr als lokale functionen erscheinen zu lassen, indem wir eine operator () – Überladung und eine anonyme class verwenden:

 int main() { struct { unsigned int operator() (unsigned int val) const { return val< =1 ? 1 : val*(*this)(val-1); } } fac; std::cout << fac(5) << '\n'; } 

Ich rate nicht, dies zu benutzen, es ist nur ein lustiger Trick (kann, aber ich sollte es nicht tun).


Update 2014:

Mit dem Aufkommen von C ++ 11 vor einer Weile können Sie nun lokale functionen haben, deren Syntax ein wenig an JavaScript erinnert:

 auto fac = [] (unsigned int val) { return val*42; }; 

Nein.

Was versuchst du zu machen?

Problemumgehung:

 int main(void) { struct foo { void operator()() { int a = 1; } }; foo b; b(); // call the operator() } 

Alte Antwort: Sie können, irgendwie, aber Sie müssen eine Dummy-class betrügen und verwenden:

 void moo() { class dummy { public: static void a() { printf("I'm in a!\n"); } }; dummy::a(); dummy::a(); } 

Neuere Antwort: Neuere Versionen von C ++ unterstützen auch Lambdas, um das besser / richtig zu machen. Siehe Antworten weiter oben auf der Seite.

Nein, das ist nicht erlaubt. Weder C noch C ++ unterstützen diese function standardmäßig, TonyK weist jedoch (in den Kommentaren) darauf hin, dass es Erweiterungen des GNU C-Compilers gibt, die dieses Verhalten in C ermöglichen.

Sie können keine freie function in einem anderen in C ++ definieren.

Wie andere bereits erwähnt haben, können Sie verschachtelte functionen verwenden, indem Sie die GNU-Spracherweiterungen in gcc verwenden. Wenn Sie (oder Ihr Projekt) sich an die gcc-Toolchain halten, wird Ihr Code größtenteils auf die verschiedenen Architekturen übertragen, auf die der gcc-Compiler zielt.

Wenn es jedoch eine mögliche Anforderung gibt, dass Sie möglicherweise Code mit einer anderen Toolchain kompilieren müssen, würde ich mich von solchen Erweiterungen fernhalten.


Ich würde auch vorsichtig vorgehen, wenn ich verschachtelte functionen verwende. Sie sind eine schöne Lösung für die Verwaltung der Struktur komplexer, aber zusammenhängender Code-Blöcke (deren Teile nicht für den externen / allgemeinen Gebrauch gedacht sind.) Sie sind auch sehr hilfreich bei der Kontrolle der Namespace-Verschmutzung (ein sehr reales Problem mit natürlich komplexen / lange classn in ausführlichen Sprachen.)

Aber wie alles, können sie offen für Missbrauch sein.

Es ist traurig, dass C / C ++ solche functionen nicht als Standard unterstützt. Die meisten Pascal-Varianten und Ada tun (fast alle Algol-basierten Sprachen). Gleiches gilt für JavaScript. Das gleiche gilt für moderne Sprachen wie Scala. Gleiches gilt für altehrwürdige Sprachen wie Erlang, Lisp oder Python.

Und genau wie bei C / C ++, leider, Java nicht (mit dem ich den größten Teil meines Lebens verdiene).

Ich erwähne Java hier, weil ich mehrere Poster sehe, die die Verwendung von classn und classnmethoden als Alternativen zu verschachtelten functionen vorschlagen. Und das ist auch die typische Problemumgehung in Java.

Kurze Antwort: Nein.

Dies führt dazu, künstliche, unnötige Komplexität in einer classnhierarchie einzuführen. Wenn alle Dinge gleich sind, ist es ideal, eine classnhierarchie (und ihre umfassenden Namespaces und Bereiche) zu haben, die eine tatsächliche Domäne so einfach wie möglich darstellt.

Verschachtelte functionen helfen bei der Behandlung von “privater” Komplexität innerhalb von functionen. Ohne diese Möglichkeiten sollte man versuchen, diese “private” Komplexität nicht in das classnmodell zu übertragen.

In der Software (und in jeder Ingenieursdisziplin) ist das Modellieren eine Frage der Kompromisse. Im wirklichen Leben wird es daher begründete Ausnahmen von diesen Regeln (oder eher Richtlinien) geben. Gehen Sie vorsichtig vor.

All diese Tricks sehen (mehr oder weniger) als lokale functionen aus, funktionieren aber nicht so. In einer lokalen function können Sie lokale Variablen ihrer Superfunktionen verwenden. Es ist eine Art Semi-Globals. Keine dieser Tricks kann das tun. Der nächste ist der Lambda-Trick aus C ++ 0x, aber der Abschluss ist in der Definitionszeit gebunden, nicht in der Verwendungszeit.

Sie können keine lokalen functionen in C ++ haben. C ++ 11 hat jedoch Lambdas . Lambdas sind im Grunde Variablen, die wie functionen funktionieren.

Ein Lambda hat den Typ std::function ( eigentlich ist das nicht ganz richtig , aber in den meisten Fällen kann man annehmen, dass es ist). Um diesen Typ zu verwenden, müssen Sie #include . std::function ist eine Vorlage, die als Argument den Rückgabetyp und die Argumenttypen mit der Syntax std::function . Zum Beispiel ist std::function ein Lambda, das ein int und zwei Argumente std::string , eine std::string und eine float . Die gebräuchlichste ist std::function , die nichts zurückgibt und keine Argumente annimmt.

Sobald ein Lambda deklariert ist, wird es genau wie eine normale function mit der Syntax lambda(arguments) aufgerufen.

Um ein Lambda zu definieren, verwenden Sie die Syntax [captures](arguments){code} (es gibt andere Möglichkeiten, dies zu tun, aber ich werde sie hier nicht erwähnen). arguments sind die Argumente, die Lambda benötigt, und code ist der Code, der beim Aufruf des Lambda ausgeführt werden soll. Normalerweise setzen Sie [=] oder [&] als Captures. [=] bedeutet, dass Sie alle Variablen in dem Bereich erfassen, in dem der Wert durch Wert definiert ist, was bedeutet, dass sie den Wert behalten, den sie bei der Deklaration des Lambda hatten. [&] bedeutet, dass Sie alle Variablen im Gültigkeitsbereich durch Verweis erfassen, was bedeutet, dass sie immer ihren aktuellen Wert haben, aber wenn sie aus dem Speicher gelöscht werden, stürzt das Programm ab. Hier sind einige Beispiele:

 #include  #include  int main(){ int x = 1; std::function lambda1 = [=](){ std::cout < < x << std::endl; }; std::function lambda2 = [&](){ std::cout < < x << std::endl; }; x = 2; lambda1(); //Prints 1 since that was the value of x when it was captured and x was captured by value with [=] lambda2(); //Prints 2 since that's the current value of x and x was captured by value with [&] std::function lambda3 = [](){}, lambda4 = [](){}; //I prefer to initialize these since calling an uninitialized lambda is undefined behavior. //[](){} is the empty lambda. { int y = 3; //y will be deleted from the memory at the end of this scope lambda3 = [=](){ std::cout < < y << endl; }; lambda4 = [&](){ std::cout << y << endl; }; } lambda3(); //Prints 3, since that's the value y had when it was captured lambda4(); //Causes the program to crash, since y was captured by reference and y doesn't exist anymore. //This is a bit like if you had a pointer to y which now points nowhere because y has been deleted from the memory. //This is why you should be careful when capturing by reference. return 0; } 

Sie können auch bestimmte Variablen erfassen, indem Sie deren Namen angeben. Wenn Sie nur ihren Namen angeben, werden sie nach Wert erfasst, wobei der Name mit einem & vor dem Namen als Referenz angegeben wird. Beispiel: [=, &foo] erfasst alle Variablen nach Wert außer foo das als Referenz erfasst wird, und [&, foo] erfasst alle Variablen als Referenz, mit Ausnahme von foo die durch den Wert erfasst wird. Sie können auch nur bestimmte Variablen erfassen, beispielsweise [&foo] erfasst foo Referenz und erfasst keine anderen Variablen. Sie können auch keine Variablen erfassen, indem Sie [] . Wenn Sie versuchen, eine Variable in einem Lambda zu verwenden, die Sie nicht erfasst haben, wird sie nicht kompiliert. Hier ist ein Beispiel:

 #include  int main(){ int x = 4, y = 5; std::function myLambda = [y](int z){ int xSquare = x * x; //Compiler error because x wasn't captured int ySquare = y * y; //OK because y was captured int zSquare = z * z; //OK because z is an argument of the lambda }; return 0; } 

Sie können den Wert einer Variablen, die von einem Wert innerhalb eines Lambda erfasst wurde, nicht ändern (Variablen, die durch Wert erfasst werden, haben einen const Typ innerhalb des Lambda). Um dies zu tun, müssen Sie die Variable als Referenz erfassen. Hier ist ein Beispiel:

 #include  int main(){ int x = 3, y = 5; std::function myLambda = [x, &y](){ x = 2; //Compiler error because x is captured by value and so it's of type const int inside the lambda y = 2; //OK because y is captured by reference }; x = 2; //This is of course OK because we're not inside the lambda return 0; } 

Das Aufrufen nicht initialisierter Lambdas ist ein undefiniertes Verhalten und führt normalerweise zum Absturz des Programms. Zum Beispiel, tu dies nie:

 std::function lambda; lambda(); //Undefined behavior because lambda is uninitialized 

Beispiele

Hier ist der Code für das, was Sie in Ihrer Frage mit lambdas machen wollten:

 #include  //Don't forget this, otherwise you won't be able to use the std::function type int main(){ std::function a = [](){ // code } a(); return 0; } 

Hier ist ein fortgeschrittenes Beispiel für ein Lambda:

 #include  //For std::function #include  //For std::cout int main(){ int x = 4; std::function divideByX = [x](int y){ return (float)y / (float)x; //x is a captured variable, y is an argument } std::cout < < divideByX(3) << std::endl; //Prints 0.75 return 0; } 

Lassen Sie mich hier eine Lösung für C ++ 03 veröffentlichen, die ich für die sauberste halte. *

 #define DECLARE_LAMBDA(NAME, RETURN_TYPE, FUNCTION) \ struct { RETURN_TYPE operator () FUNCTION } NAME; ... int main(){ DECLARE_LAMBDA(demoLambda, void, (){ cout< <"I'm a lambda!"<
		      	

Aber wir können eine function in main () deklarieren:

 int main() { void a(); } 

Obwohl die Syntax korrekt ist, kann es manchmal zum “Most vexing parse” führen:

 #include  struct U { U() : val(0) {} U(int val) : val(val) {} int val; }; struct V { V(U a, U b) { std::cout < < "V(" << a.val << ", " << b.val << ");\n"; } ~V() { std::cout << "~V();\n"; } }; int main() { int five = 5; V v(U(five), U()); } 

=> keine Programmausgabe.

(Nur Clangwarnung nach der Kompilierung).

C ++ ist wieder am ärgerlichsten Parse

Wenn Sie versuchen, eine function in einem anderen functionskörper zu implementieren, müssen Sie diesen error als ungültige Definition erhalten:

 error C2601: 'a' : local function definitions are illegal IntelliSense: expected a ';' 

Also versuche es nicht noch einmal.