Intendo qualcosa come:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Intendo qualcosa come:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Risposte:
Nelle versioni correnti di c ++ (C ++ 11, C ++ 14 e C ++ 17), è possibile avere funzioni all'interno di funzioni sotto forma di 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 può anche modificare le variabili locali tramite ** cattura per riferimento *. Con l'acquisizione per riferimento, lambda ha accesso a tutte le variabili locali dichiarate nell'ambito di lambda. Può modificarli e cambiarli normalmente.
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 ++ non lo supporta direttamente.
Detto questo, puoi avere classi locali e possono avere funzioni (non static
o static
), quindi puoi farlo in qualche misura, anche se è un po 'un kludge:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Tuttavia, metterei in discussione la prassi. Tutti sanno (bene, ora che lo fai, comunque :)
) C ++ non supporta le funzioni locali, quindi sono abituati a non averle. Non sono tuttavia abituati a quel kludge. Vorrei dedicare un po 'di tempo a questo codice per assicurarmi che sia davvero lì solo per consentire le funzioni locali. Non bene.
int main()
eint main(int argc, char* argv[])
int main()
e int main(int argc, char* argv[])
deve essere supportato e altri possono essere supportati ma tutti hanno un int di ritorno.
A tutti gli effetti, C ++ supporta questo tramite lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Qui, f
c'è un oggetto lambda che funge da funzione locale in main
. È possibile specificare le acquisizioni per consentire alla funzione di accedere agli oggetti locali.
Dietro le quinte, f
c'è un oggetto funzione (cioè un oggetto di un tipo che fornisce un operator()
). Il tipo di oggetto funzione viene creato dal compilatore basato su lambda.
1 dal C ++ 11
+1
da parte mia.
Le classi locali sono già state menzionate, ma qui è un modo per farle apparire ancora di più come funzioni locali, usando un sovraccarico operatore () e una classe anonima:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Non consiglio di usare questo, è solo un trucco divertente (può fare, ma non dovrei).
Con l'ascesa di C ++ 11 qualche tempo fa, ora puoi avere funzioni locali la cui sintassi ricorda un po 'JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, ti manca un set di parentesi.
std::sort()
, o std::for_each()
.
auto
per dichiarare la variabile. Stroustrup fornisce l'esempio: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
per invertire una stringa dati i puntatori di inizio e fine.
No.
Cosa stai cercando di fare?
soluzione alternativa:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
A partire da C ++ 11 è possibile utilizzare lambda appropriate . Vedi le altre risposte per maggiori dettagli.
Vecchia risposta: puoi, in un certo senso, ma devi imbrogliare e usare una classe fittizia:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Come altri hanno già detto, puoi usare le funzioni nidificate usando le estensioni del linguaggio gnu in gcc. Se tu (o il tuo progetto) segui la toolchain di gcc, il tuo codice sarà per lo più portatile attraverso le diverse architetture targetizzate dal compilatore gcc.
Tuttavia, se esiste un possibile requisito che potrebbe essere necessario compilare il codice con una toolchain diversa, allora starei lontano da tali estensioni.
Avrei anche camminato con cura quando usavo le funzioni annidate. Sono una bella soluzione per gestire la struttura di blocchi di codice complessi ma coerenti (i cui pezzi non sono pensati per uso esterno / generale.) Sono anche molto utili nel controllo dell'inquinamento dello spazio dei nomi (una preoccupazione molto reale con naturalmente complessi / lunghe lezioni in lingue verbose).
Ma come qualsiasi cosa, possono essere aperti agli abusi.
È triste che C / C ++ non supporti tali funzionalità come standard. La maggior parte delle varianti pasquali e Ada fanno (quasi tutte le lingue basate su Algol lo fanno). Lo stesso con JavaScript. Lo stesso vale per le lingue moderne come la Scala. Lo stesso con linguaggi venerabili come Erlang, Lisp o Python.
E proprio come con C / C ++, purtroppo Java (con il quale guadagno la maggior parte della mia vita) non lo fa.
Cito Java qui perché vedo diversi poster che suggeriscono l'uso di classi e metodi di classe come alternative alle funzioni nidificate. E questa è anche la soluzione alternativa tipica in Java.
Risposta breve: No.
In questo modo si tende a introdurre una complessità artificiale e inutile in una gerarchia di classi. A parità di condizioni, l'ideale è disporre di una gerarchia di classi (e dei relativi spazi dei nomi e ambiti) che rappresenti un dominio effettivo il più semplice possibile.
Le funzioni nidificate aiutano a gestire la complessità "privata" e all'interno delle funzioni. In mancanza di tali strutture, si dovrebbe cercare di evitare di propagare quella complessità "privata" fuori e nel proprio modello di classe.
Nel software (e in qualsiasi disciplina di ingegneria), la modellazione è una questione di compromessi. Pertanto, nella vita reale, ci saranno giustificate eccezioni a tali regole (o meglio alle linee guida). Procedere con cura, però.
Non puoi avere funzioni locali in C ++. Tuttavia, C ++ 11 ha lambdas . Le lambda sono sostanzialmente variabili che funzionano come funzioni.
Una lambda ha il tipo std::function
(in realtà non è del tutto vero , ma nella maggior parte dei casi puoi supporre che lo sia). Per utilizzare questo tipo, è necessario #include <functional>
. std::function
è un modello, prendendo come argomento modello il tipo restituito e i tipi di argomento, con la sintassi std::function<ReturnType(ArgumentTypes)
. Ad esempio, std::function<int(std::string, float)>
è una lambda che restituisce un int
e accetta due argomenti, uno std::string
e uno float
. Il più comune è std::function<void()>
, che non restituisce nulla e non accetta argomenti.
Una volta dichiarata una lambda, viene chiamata proprio come una normale funzione, usando la sintassi lambda(arguments)
.
Per definire un lambda, usa la sintassi [captures](arguments){code}
(ci sono altri modi per farlo, ma non li menzionerò qui). arguments
è quali argomenti prende lambda ed code
è il codice che dovrebbe essere eseguito quando viene chiamato lambda. Di solito metti [=]
o [&]
come catture. [=]
significa che acquisisci tutte le variabili nell'ambito in cui il valore è definito dal valore, il che significa che manterranno il valore che avevano quando è stata dichiarata la lambda. [&]
significa che si acquisiscono tutte le variabili nell'ambito per riferimento, il che significa che avranno sempre il loro valore corrente, ma se vengono cancellate dalla memoria il programma andrà in crash. Ecco alcuni esempi:
#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;
}
Puoi anche acquisire variabili specifiche specificandone i nomi. Basta specificare il loro nome per catturarli per valore, specificando il loro nome con un &
prima li cattureranno per riferimento. Ad esempio, [=, &foo]
acquisirà tutte le variabili in base al valore tranne quelle foo
che verranno acquisite per riferimento e [&, foo]
acquisirà tutte le variabili in base al riferimento tranne quelle foo
che verranno acquisite in base al valore. È anche possibile acquisire solo variabili specifiche, ad esempio [&foo]
acquisirà foo
per riferimento e non acquisirà altre variabili. Puoi anche non catturare alcuna variabile usando []
. Se si tenta di utilizzare una variabile in una lambda che non è stata acquisita, non verrà compilata. Ecco un esempio:
#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;
}
Non è possibile modificare il valore di una variabile catturata dal valore all'interno di un lambda (le variabili catturate dal valore hanno un const
tipo all'interno del lambda). Per fare ciò, è necessario acquisire la variabile per riferimento. Ecco un esempio:
#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;
}
Inoltre, chiamare lambda non inizializzati è un comportamento indefinito e di solito causa l'arresto anomalo del programma. Ad esempio, non farlo mai:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Esempi
Ecco il codice per quello che volevi fare nella tua domanda usando 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;
}
Ecco un esempio più avanzato di 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;
}
No, non è permesso. Né C né C ++ supportano questa funzionalità per impostazione predefinita, tuttavia TonyK sottolinea (nei commenti) che esistono estensioni del compilatore C GNU che abilitano questo comportamento in C.
Tutti questi trucchi sembrano (più o meno) funzioni locali, ma non funzionano così. In una funzione locale puoi usare le variabili locali delle sue super funzioni. È una specie di semi-globali. Nessuno di questi trucchi può farlo. Il più vicino è il trucco lambda di c ++ 0x, ma la sua chiusura è limitata nel tempo di definizione, non nel tempo di utilizzo.
Non è possibile definire una funzione libera all'interno di un'altra in C ++.
Consentitemi di pubblicare una soluzione qui per C ++ 03 che considero il più pulito possibile. *
#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;
}
(*) nel mondo C ++ l'uso di macro non è mai considerato pulito.
Ma possiamo dichiarare una funzione dentro main ():
int main()
{
void a();
}
Sebbene la sintassi sia corretta, a volte può portare all'analisi più fastidiosa:
#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());
}
=> nessun output del programma.
(Solo avvertimento Clang dopo la compilazione).