Ich meine so etwas wie:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Ich meine so etwas wie:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Antworten:
In aktuellen Versionen von c ++ (C ++ 11, C ++ 14 und C ++ 17) können Sie Funktionen in Funktionen in Form eines Lambda haben:
int main() {
// This declares a lambda, which can be called just like a function
auto print_message = [](std::string message)
{
std::cout << message << "\n";
};
// Prints "Hello!" 10 times
for(int i = 0; i < 10; i++) {
print_message("Hello!");
}
}
Lambdas kann lokale Variablen auch über ** Capture-by-Reference * ändern. Mit der Erfassung durch Referenz hat das Lambda Zugriff auf alle lokalen Variablen, die im Gültigkeitsbereich des Lambda deklariert sind. Es kann sie normal modifizieren und ändern.
int main() {
int i = 0;
// Captures i by reference; increments it by one
auto addOne = [&] () {
i++;
};
while(i < 10) {
addOne(); //Add 1 to i
std::cout << i << "\n";
}
}
C ++ unterstützt das nicht direkt.
Das heißt, Sie können lokale Klassen haben, und sie können Funktionen haben (nicht static
oder static
), so dass Sie dies bis zu einem gewissen Grad erhalten können, obwohl es ein bisschen kludge 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ß (nun, da Sie es sowieso tun :)
), dass C ++ keine lokalen Funktionen unterstützt, daher sind sie es gewohnt, sie nicht zu haben. Sie sind jedoch nicht an diesen Kludge gewöhnt. Ich würde eine ganze Weile mit diesem Code verbringen, um sicherzustellen, dass er wirklich nur lokale Funktionen zulässt. Nicht gut.
int main()
undint main(int argc, char* argv[])
int main()
und int main(int argc, char* argv[])
muss unterstützt werden und andere können unterstützt werden, aber alle haben return int.
In jeder Hinsicht unterstützt C ++ dies über Lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Hier f
ist ein Lambda-Objekt, das als lokale Funktion in fungiert main
. Es können Erfassungen angegeben werden, damit die Funktion auf lokale Objekte zugreifen kann.
Hinter den Kulissen f
befindet sich ein Funktionsobjekt (dh ein Objekt eines Typs, der ein bereitstellt operator()
). Der Funktionsobjekttyp wird vom Compiler basierend auf dem Lambda erstellt.
1 seit C ++ 11
+1
von mir.
Lokale Klassen wurden bereits erwähnt, aber hier ist eine Möglichkeit, sie mithilfe einer operator () -Überladung und einer anonymen Klasse noch mehr als lokale Funktionen erscheinen zu lassen:
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 dazu, es ist nur ein lustiger Trick (kann, aber imho sollte nicht).
Mit dem Aufstieg von C ++ 11 vor einiger Zeit können Sie jetzt lokale Funktionen haben, deren Syntax ein wenig an JavaScript erinnert:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, fehlt Ihnen ein Satz von Klammern.
std::sort()
oder übergeben müssen std::for_each()
.
auto
die Variable jedoch nicht deklarieren. Stroustrup gibt das Beispiel: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
Zum Umkehren einer Zeichenfolge mit Anfangs- und Endzeigern.
Nein.
Was versuchst du zu machen?
Problemumgehung:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
Ab C ++ 11 können Sie geeignete Lambdas verwenden . Weitere Details finden Sie in den anderen Antworten.
Alte Antwort: Sie können, irgendwie, aber Sie müssen betrügen und eine Dummy-Klasse verwenden:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Wie bereits erwähnt, können Sie verschachtelte Funktionen mithilfe der gnu-Spracherweiterungen in gcc verwenden. Wenn Sie (oder Ihr Projekt) sich an die gcc-Toolchain halten, kann Ihr Code hauptsächlich auf die verschiedenen Architekturen übertragen werden, auf die der gcc-Compiler abzielt.
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 Funktionen verwende. Sie sind eine schöne Lösung für die Verwaltung der Struktur komplexer, aber zusammenhängender Codeblöcke (deren Teile nicht für den externen / allgemeinen Gebrauch bestimmt sind). Sie sind auch sehr hilfreich bei der Kontrolle der Verschmutzung von Namespaces (ein sehr reales Problem bei natürlich komplexen /. lange Klassen in ausführlichen Sprachen.)
Aber wie alles andere können sie für Missbrauch offen sein.
Es ist traurig, dass C / C ++ solche Funktionen nicht als Standard unterstützt. Die meisten Pascal-Varianten und Ada tun dies (fast alle Algol-basierten Sprachen). Gleiches gilt für JavaScript. Gleiches gilt für moderne Sprachen wie Scala. Gleiches gilt für ehrwürdige Sprachen wie Erlang, Lisp oder Python.
Und genau wie bei C / C ++ funktioniert Java (mit dem ich den größten Teil meines Lebensunterhalts verdiene) leider nicht.
Ich erwähne Java hier, weil ich mehrere Poster sehe, die die Verwendung von Klassen und Klassenmethoden als Alternativen zu verschachtelten Funktionen vorschlagen. Und das ist auch die typische Problemumgehung in Java.
Kurze Antwort: Nein.
Dies führt tendenziell zu einer künstlichen, unnötigen Komplexität einer Klassenhierarchie. Wenn alle Dinge gleich sind, ist es ideal, eine Klassenhierarchie (und ihre umfassenden Namespaces und Bereiche) zu haben, die eine tatsächliche Domäne so einfach wie möglich darstellt.
Verschachtelte Funktionen helfen bei der Bewältigung der "privaten" Komplexität innerhalb der Funktion. Ohne diese Einrichtungen sollte man versuchen zu vermeiden, diese "private" Komplexität in das eigene Klassenmodell zu übertragen.
In der Software (und in jeder technischen Disziplin) ist die Modellierung eine Frage von Kompromissen. Daher wird es im wirklichen Leben gerechtfertigte Ausnahmen von diesen Regeln (oder vielmehr Richtlinien) geben. Gehen Sie jedoch vorsichtig vor.
Sie können keine lokalen Funktionen in C ++ haben. C ++ 11 hat jedoch Lambdas . Lambdas sind im Grunde genommen Variablen, die wie Funktionen funktionieren.
Ein Lambda hat den Typ std::function
( eigentlich stimmt das nicht ganz , aber in den meisten Fällen kann man davon ausgehen). Um diesen Typ zu verwenden, müssen Sie #include <functional>
. std::function
ist eine Vorlage, die als Vorlagenargument den Rückgabetyp und die Argumenttypen mit der Syntax verwendet std::function<ReturnType(ArgumentTypes)
. Zum Beispiel std::function<int(std::string, float)>
gibt ein Lambda ein zurück int
und nimmt zwei Argumente an, eins std::string
und eins float
. Die häufigste ist std::function<void()>
, dass sie nichts zurückgibt und keine Argumente akzeptiert.
Sobald ein Lambda deklariert ist, wird es wie eine normale Funktion unter Verwendung der Syntax aufgerufen lambda(arguments)
.
Verwenden Sie zum Definieren eines Lambda die Syntax [captures](arguments){code}
(es gibt andere Möglichkeiten, dies zu tun, aber ich werde sie hier nicht erwähnen). arguments
Dies sind die Argumente, die das Lambda verwendet, und code
der Code, der ausgeführt werden soll, wenn das Lambda aufgerufen wird. Normalerweise setzen Sie [=]
oder [&]
als Captures. [=]
bedeutet, dass Sie alle Variablen in dem Bereich erfassen, in dem der Wert durch value definiert ist. Dies bedeutet, dass sie den Wert beibehalten, den sie hatten, als das Lambda deklariert wurde. [&]
bedeutet, dass Sie alle Variablen im Bereich als Referenz erfassen. Dies bedeutet, dass sie immer ihren aktuellen Wert haben. Wenn sie jedoch aus dem Speicher gelöscht werden, stürzt das Programm ab. Hier sind einige Beispiele:
#include <functional>
#include <iostream>
int main(){
int x = 1;
std::function<void()> lambda1 = [=](){
std::cout << x << std::endl;
};
std::function<void()> 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<void()> 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 bestimmte Variablen auch erfassen, indem Sie deren Namen angeben. Wenn Sie nur ihren Namen angeben, werden sie nach Wert erfasst. Wenn Sie ihren Namen mit einem &
Vorher angeben, werden sie als Referenz erfasst. Erfasst beispielsweise [=, &foo]
alle Variablen nach Wert, außer foo
denen, die als Referenz erfasst werden, und [&, foo]
erfasst alle Variablen nach Referenz, außer foo
die nach Wert erfasst werden. Sie können auch nur bestimmte Variablen erfassen, z. B. [&foo]
als foo
Referenz und keine anderen Variablen. Sie können mit überhaupt auch überhaupt keine Variablen erfassen []
. Wenn Sie versuchen, eine Variable in einem Lambda zu verwenden, die Sie nicht erfasst haben, wird sie nicht kompiliert. Hier ist ein Beispiel:
#include <functional>
int main(){
int x = 4, y = 5;
std::function<void(int)> 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 durch den Wert in einem Lambda erfasst wurde, nicht ändern (Variablen, die durch den Wert erfasst werden, haben einen const
Typ innerhalb des Lambda). Dazu müssen Sie die Variable als Referenz erfassen. Hier ist ein Beispiel:
#include <functional>
int main(){
int x = 3, y = 5;
std::function<void()> 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 von nicht initialisierten Lambdas ist ein undefiniertes Verhalten und führt normalerweise zum Absturz des Programms. Tun Sie zum Beispiel niemals Folgendes:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Beispiele
Hier ist der Code für das, was Sie in Ihrer Frage mit Lambdas tun wollten:
#include <functional> //Don't forget this, otherwise you won't be able to use the std::function type
int main(){
std::function<void()> a = [](){
// code
}
a();
return 0;
}
Hier ist ein fortgeschritteneres Beispiel für ein Lambda:
#include <functional> //For std::function
#include <iostream> //For std::cout
int main(){
int x = 4;
std::function<float(int)> 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;
}
Nein, das ist nicht erlaubt. Weder C noch C ++ unterstützen diese Funktion standardmäßig. TonyK weist jedoch (in den Kommentaren) darauf hin, dass es Erweiterungen für den GNU C-Compiler gibt, die dieses Verhalten in C ermöglichen.
All diese Tricks sehen (mehr oder weniger) nur als lokale Funktionen aus, funktionieren aber nicht so. In einer lokalen Funktion können Sie lokale Variablen ihrer Superfunktionen verwenden. Es ist eine Art Halbglobale. Keiner dieser Tricks kann das. Der nächste ist der Lambda-Trick aus c ++ 0x, aber sein Abschluss ist an die Definitionszeit gebunden, nicht an die Nutzungszeit.
Sie können in C ++ keine freie Funktion in einer anderen definieren.
Lassen Sie mich hier eine Lösung für C ++ 03 veröffentlichen, die ich für möglichst sauber 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!"<<endl; });
demoLambda();
DECLARE_LAMBDA(plus, int, (int i, int j){
return i+j;
});
cout << "plus(1,2)=" << plus(1,2) << endl;
return 0;
}
(*) In der C ++ - Welt wird die Verwendung von Makros niemals als sauber angesehen.
Aber wir können eine Funktion in main () deklarieren:
int main()
{
void a();
}
Obwohl die Syntax korrekt ist, kann sie manchmal zur "ärgerlichsten Analyse" führen:
#include <iostream>
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 Clang-Warnung nach der Kompilierung).