Mam na myśli coś takiego:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Mam na myśli coś takiego:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Odpowiedzi:
W aktualnych wersjach c ++ (C ++ 11, C ++ 14 i C ++ 17) możesz mieć funkcje wewnątrz funkcji w postaci lambda:
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 może również modyfikować zmienne lokalne poprzez ** przechwytywanie przez odniesienie *. Dzięki przechwytywaniu przez referencję lambda ma dostęp do wszystkich zmiennych lokalnych zadeklarowanych w zakresie lambda. Może modyfikować i zmieniać je normalnie.
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 ++ nie obsługuje tego bezpośrednio.
To powiedziawszy, możesz mieć lokalne klasy i mogą one mieć funkcje (nie staticlub static), więc możesz to zrobić w pewnym stopniu, choć to trochę kłopot:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Chciałbym jednak zakwestionować praktykę. Wszyscy wiedzą (cóż, teraz, kiedy to robisz :)) C ++ nie obsługuje funkcji lokalnych, więc są przyzwyczajeni do ich braku. Nie są one jednak używane do tej kludge. Spędziłbym sporo czasu na tym kodzie, aby upewnić się, że tak naprawdę jest tylko tam, aby umożliwić lokalne funkcje. Niedobrze.
int main()iint main(int argc, char* argv[])
int main()i int main(int argc, char* argv[])musi być obsługiwany, a inne mogą być obsługiwane, ale wszystkie mają return int.
We wszystkich zamiarach i celach C ++ obsługuje to poprzez lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Tutaj fjest obiekt lambda, który działa jako funkcja lokalna w main. Można określić przechwyty, aby umożliwić tej funkcji dostęp do obiektów lokalnych.
Za kulisami fznajduje się obiekt funkcji (tj. Obiekt typu, który zapewnia an operator()). Typ obiektu funkcji jest tworzony przez kompilator na podstawie lambda.
1 od C ++ 11
+1ode mnie.
Klasy lokalne zostały już wspomniane, ale tutaj jest sposób, aby pojawiały się jeszcze bardziej jako funkcje lokalne, wykorzystując przeciążenie operatora () i anonimową klasę:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Nie radzę tego używać, to tylko zabawna sztuczka (może zrobić, ale imho nie powinna).
Wraz z pojawieniem się C ++ 11 jakiś czas temu możesz teraz mieć funkcje lokalne, których składnia przypomina trochę JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val), brakuje Ci zestawu nawiasów.
std::sort()lub std::for_each().
autodo zadeklarowania zmiennej. Stroustrup podaje przykład: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };do odwrócenia ciągu o podanych wskaźnikach początku i końca.
Nie.
Co próbujesz zrobić?
obejście:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
Począwszy od C ++ 11 możesz używać odpowiednich lambd . Zobacz inne odpowiedzi, aby uzyskać więcej informacji.
Stara odpowiedź: możesz posortować, ale musisz oszukiwać i używać manekina:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Jak wspomnieli inni, możesz używać funkcji zagnieżdżonych za pomocą rozszerzeń języka gnu w gcc. Jeśli ty (lub twój projekt) trzymasz się łańcucha narzędziowego gcc, twój kod będzie w większości przenośny w różnych architekturach docelowych kompilatora gcc.
Jeśli jednak istnieje możliwość, że konieczne może być skompilowanie kodu przy użyciu innego zestawu narzędzi, to trzymam się z dala od takich rozszerzeń.
Podczas używania funkcji zagnieżdżonych również ostrożnie podążałem. Są pięknym rozwiązaniem do zarządzania strukturą złożonych, ale spójnych bloków kodu (których fragmenty nie są przeznaczone do użytku zewnętrznego / ogólnego). Są również bardzo pomocne w kontrolowaniu zanieczyszczenia przestrzeni nazw (bardzo realna sprawa z naturalnie złożonymi / długie lekcje w pełnych językach).
Ale jak wszystko, mogą być podatni na nadużycia.
Szkoda, że C / C ++ nie obsługuje takich funkcji jako standard. Większość wariantów Pascala i Ada ma (prawie wszystkie języki oparte na Algolu). To samo z JavaScript. To samo z nowoczesnymi językami, takimi jak Scala. To samo z czcigodnymi językami, takimi jak Erlang, Lisp lub Python.
I podobnie jak w przypadku C / C ++, niestety Java (dzięki której zarabiam większość życia) nie.
Wspominam tutaj o Javie, ponieważ widzę kilka plakatów sugerujących użycie klas i metod klas jako alternatywy dla funkcji zagnieżdżonych. Jest to również typowe obejście w Javie.
Krótka odpowiedź: nie
W ten sposób wprowadza się sztuczną, niepotrzebną złożoność w hierarchii klas. Ponieważ wszystkie rzeczy są równe, idealnym rozwiązaniem jest posiadanie hierarchii klas (i obejmujących przestrzenie nazw i zakresy) reprezentującej rzeczywistą domenę tak prosto, jak to możliwe.
Funkcje zagnieżdżone pomagają radzić sobie z „prywatną” złożonością wewnątrzfunkcyjną. Bez tych udogodnień należy starać się unikać propagowania tej „prywatnej” złożoności do własnego modelu klasowego.
W oprogramowaniu (i dowolnej dyscyplinie inżynierskiej) modelowanie jest kwestią kompromisów. Tak więc w prawdziwym życiu będą uzasadnione wyjątki od tych zasad (a raczej wytycznych). Postępuj jednak ostrożnie.
Nie możesz mieć funkcji lokalnych w C ++. Jednak C ++ 11 ma lambdas . Lambda to zasadniczo zmienne, które działają jak funkcje.
Lambda ma typ std::function( właściwie to nie do końca prawda , ale w większości przypadków można przypuszczać, że tak). Aby użyć tego typu, musisz #include <functional>. std::functionjest szablonem, przyjmującym jako argument szablonu typ zwracany i typy argumentów ze składnią std::function<ReturnType(ArgumentTypes). Na przykład, std::function<int(std::string, float)>lambda zwraca inti przyjmuje dwa argumenty, jeden std::stringi jeden float. Najczęstszym jest ten std::function<void()>, który nic nie zwraca i nie przyjmuje argumentów.
Po zadeklarowaniu lambda jest ona wywoływana tak jak normalna funkcja, przy użyciu składni lambda(arguments).
Aby zdefiniować lambda, użyj składni [captures](arguments){code}(są na to inne sposoby, ale nie wspomnę o nich tutaj). argumentsto argumenty, które przyjmuje lambda, i codeto kod, który powinien być uruchamiany, gdy lambda jest wywoływana. Zwykle wkładasz [=]lub [&]jako przechwytywanie. [=]oznacza, że przechwytujesz wszystkie zmienne w zakresie, w którym wartość jest zdefiniowana przez wartość, co oznacza, że zachowają wartość, którą mieli, kiedy zadeklarowano lambda. [&]oznacza, że przechwytujesz wszystkie zmienne w zakresie przez odniesienie, co oznacza, że zawsze będą miały swoją bieżącą wartość, ale jeśli zostaną usunięte z pamięci, program się zawiesi. Oto kilka przykładów:
#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;
}
Możesz także uchwycić określone zmienne, podając ich nazwy. Wystarczy podać ich nazwę, aby uchwycić je według wartości, a określenie ich za pomocą &wcześniej spowoduje przechwycenie ich przez odniesienie. Na przykład [=, &foo]przechwyci wszystkie zmienne według wartości, z wyjątkiem tych, fooktóre zostaną przechwycone przez odniesienie, i [&, foo]przechwyci wszystkie zmienne przez odniesienie, z wyjątkiem tych, fooktóre zostaną przechwycone przez wartość. Możesz także przechwytywać tylko określone zmienne, na przykład [&foo]przechwytuje się fooprzez odniesienie i nie przechwytuje żadnych innych zmiennych. Za pomocą można także przechwytywać żadnych zmiennych []. Jeśli spróbujesz użyć zmiennej w lambdzie, której nie przechwyciłeś, nie zostanie skompilowana. Oto przykład:
#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;
}
Nie można zmienić wartości zmiennej, która została przechwycona przez wartość wewnątrz lambda (zmienne przechwycone przez wartość mają consttyp wewnątrz lambda). Aby to zrobić, musisz uchwycić zmienną przez odniesienie. Oto przykład:
#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;
}
Ponadto wywoływanie niezainicjowanych lambd jest nieokreślonym zachowaniem i zwykle powoduje awarię programu. Na przykład nigdy nie rób tego:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Przykłady
Oto kod tego, co chcesz zrobić w swoim pytaniu za pomocą lambdas:
#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;
}
Oto bardziej zaawansowany przykład 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;
}
Nie, to niedozwolone. Ani C, ani C ++ domyślnie nie obsługują tej funkcji, jednak TonyK wskazuje (w komentarzach), że istnieją rozszerzenia kompilatora GNU C, które umożliwiają takie zachowanie w C.
Wszystkie te sztuczki wyglądają (mniej więcej) jako funkcje lokalne, ale tak nie działają. W funkcji lokalnej możesz używać zmiennych lokalnych jej superfunkcji. To rodzaj półglobali. Żadne z tych sztuczek może to zrobić. Najbliższa jest sztuczka lambda z c ++ 0x, ale jej zamknięcie jest ograniczone czasem definicji, a nie czasem użycia.
Nie możesz zdefiniować wolnej funkcji wewnątrz innej w C ++.
Pozwól, że opublikuję tutaj rozwiązanie dla C ++ 03, które uważam za najczystsze z możliwych. *
#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;
}
(*) w świecie C ++ używanie makr nigdy nie jest uważane za czyste.
Ale możemy zadeklarować funkcję wewnątrz main ():
int main()
{
void a();
}
Chociaż składnia jest poprawna, czasami może prowadzić do „najbardziej dokuczliwej analizy”:
#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());
}
=> brak danych wyjściowych programu.
(Tylko ostrzeżenie Clang po kompilacji).