Je veux dire quelque chose comme:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Je veux dire quelque chose comme:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Réponses:
Dans les versions actuelles de c ++ (C ++ 11, C ++ 14 et C ++ 17), vous pouvez avoir des fonctions à l'intérieur de fonctions sous la forme d'un 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 peut également modifier des variables locales via ** capture-by-reference *. Avec capture par référence, le lambda a accès à toutes les variables locales déclarées dans la portée du lambda. Il peut les modifier et les changer normalement.
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 ++ ne prend pas cela directement en charge.
Cela dit, vous pouvez avoir des classes locales et elles peuvent avoir des fonctions (non static
ou static
), vous pouvez donc obtenir cela dans une certaine mesure, bien que ce soit un peu compliqué:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Cependant, je remettrais en question la pratique. Tout le monde sait (enfin, maintenant que vous le faites, de toute façon :)
) C ++ ne prend pas en charge les fonctions locales, ils sont donc habitués à ne pas les avoir. Ils ne sont cependant pas habitués à cette complaisance. Je passerais pas mal de temps sur ce code pour m'assurer qu'il n'est vraiment là que pour autoriser les fonctions locales. Pas bon.
int main()
etint main(int argc, char* argv[])
int main()
et int main(int argc, char* argv[])
doit être prise en charge et d'autres peuvent être prises en charge, mais elles ont toutes return int.
À toutes fins utiles, C ++ prend en charge cela via lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Voici f
un objet lambda qui agit comme une fonction locale dansmain
. Des captures peuvent être spécifiées pour permettre à la fonction d'accéder aux objets locaux.
Dans les coulisses, se f
trouve un objet fonction (c'est-à-dire un objet d'un type qui fournit un operator()
). Le type d'objet fonction est créé par le compilateur sur la base du lambda.
1 depuis C ++ 11
+1
de ma part.
Les classes locales ont déjà été mentionnées, mais voici un moyen de les faire apparaître encore plus comme des fonctions locales, en utilisant une surcharge operator () et une classe anonyme:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
Je ne conseille pas d'utiliser cela, c'est juste une astuce amusante (peut le faire, mais à mon humble avis ne devrait pas).
Avec la montée en puissance de C ++ 11 il y a quelque temps, vous pouvez maintenant avoir des fonctions locales dont la syntaxe rappelle un peu le JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val)
, il vous manque un jeu de parenthèses.
std::sort()
ou std::for_each()
.
auto
pour déclarer la variable. Stroustrup donne l'exemple: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };
pour inverser une chaîne avec des pointeurs de début et de fin donnés.
Non.
Qu'essayez-vous de faire?
solution de contournement:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
À partir de C ++ 11, vous pouvez utiliser des lambdas appropriés . Voir les autres réponses pour plus de détails.
Ancienne réponse: vous pouvez, en quelque sorte, mais vous devez tricher et utiliser une classe factice:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Comme d'autres l'ont mentionné, vous pouvez utiliser des fonctions imbriquées en utilisant les extensions de langage gnu dans gcc. Si vous (ou votre projet) vous en tenez à la chaîne d'outils gcc, votre code sera principalement portable sur les différentes architectures ciblées par le compilateur gcc.
Cependant, s'il est possible que vous deviez compiler du code avec une chaîne d'outils différente, je resterais à l'écart de ces extensions.
Je ferais également preuve de prudence lors de l'utilisation de fonctions imbriquées. Ils sont une belle solution pour gérer la structure de blocs de code complexes, mais cohérents (dont les morceaux ne sont pas destinés à un usage externe / général.) Ils sont également très utiles pour contrôler la pollution de l'espace de noms (une préoccupation très réelle avec naturellement complexe / longues classes dans des langues verbeuses.)
Mais comme tout, ils peuvent être ouverts à des abus.
Il est regrettable que C / C ++ ne prenne pas en charge ces fonctionnalités en tant que norme. La plupart des variantes pascal et Ada font (presque toutes les langues basées sur Algol font). Même chose avec JavaScript. Même chose avec les langues modernes comme Scala. Idem avec les langages vénérables comme Erlang, Lisp ou Python.
Et tout comme avec C / C ++, malheureusement, Java (avec lequel je gagne la majeure partie de ma vie) ne le fait pas.
Je mentionne Java ici parce que je vois plusieurs affiches suggérant l'utilisation de classes et de méthodes de classe comme alternatives aux fonctions imbriquées. Et c'est aussi la solution de contournement typique de Java.
Réponse courte: Non.
Cela tend à introduire une complexité artificielle et inutile dans une hiérarchie de classes. Toutes choses étant égales par ailleurs, l'idéal est d'avoir une hiérarchie de classes (et ses espaces de noms et portées englobants) représentant un domaine réel aussi simple que possible.
Les fonctions imbriquées aident à gérer la complexité «privée» au sein de la fonction. Faute de ces installations, on devrait essayer d'éviter de propager cette complexité «privée» à l'extérieur et dans le modèle de classe.
Dans le logiciel (et dans toute discipline d'ingénierie), la modélisation est une question de compromis. Ainsi, dans la vie réelle, il y aura des exceptions justifiées à ces règles (ou plutôt directives). Procédez avec prudence, cependant.
Vous ne pouvez pas avoir de fonctions locales en C ++. Cependant, C ++ 11 a des lambdas . Les lambdas sont essentiellement des variables qui fonctionnent comme des fonctions.
Un lambda a le type std::function
(en fait, ce n'est pas tout à fait vrai , mais dans la plupart des cas, vous pouvez le supposer). Pour utiliser ce type, vous devez #include <functional>
. std::function
est un modèle, prenant comme argument de modèle le type de retour et les types d'argument, avec la syntaxe std::function<ReturnType(ArgumentTypes)
. Par exemple, std::function<int(std::string, float)>
un lambda renvoie un int
et prend deux arguments, un std::string
et unfloat
. Le plus courant est celui std::function<void()>
qui ne renvoie rien et ne prend aucun argument.
Une fois qu'un lambda est déclaré, il est appelé comme une fonction normale, en utilisant la syntaxe lambda(arguments)
.
Pour définir un lambda, utilisez la syntaxe [captures](arguments){code}
(il existe d'autres façons de le faire, mais je ne les mentionnerai pas ici). arguments
est le type d'arguments pris par le lambda, et code
c'est le code qui doit être exécuté lorsque le lambda est appelé. Habituellement, vous mettez [=]
ou en [&]
tant que captures. [=]
signifie que vous capturez toutes les variables dans la portée dans laquelle la valeur est définie par valeur, ce qui signifie qu'elles conserveront la valeur qu'elles avaient lorsque la lambda a été déclarée. [&]
signifie que vous capturez toutes les variables dans la portée par référence, ce qui signifie qu'elles auront toujours leur valeur actuelle, mais si elles sont effacées de la mémoire, le programme se bloquera. Voici quelques exemples:
#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;
}
Vous pouvez également capturer des variables spécifiques en spécifiant leurs noms. La simple spécification de leur nom les capturera par valeur, la spécification de leur nom avec un &
avant les capturera par référence. Par exemple, [=, &foo]
capturera toutes les variables par valeur, sauf celles foo
qui seront capturées par référence, et [&, foo]
capturera toutes les variables par référence, sauf celles foo
qui seront capturées par valeur. Vous pouvez également capturer uniquement des variables spécifiques, par exemple [&foo]
capturer foo
par référence et ne capturer aucune autre variable. Vous pouvez également capturer aucune variable du tout en utilisant []
. Si vous essayez d'utiliser une variable dans un lambda que vous n'avez pas capturée, elle ne sera pas compilée. Voici un exemple:
#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;
}
Vous ne pouvez pas modifier la valeur d'une variable qui a été capturée par la valeur à l'intérieur d'un lambda (les variables capturées par la valeur ont un const
type à l'intérieur du lambda). Pour ce faire, vous devez capturer la variable par référence. Voici un exemple:
#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;
}
En outre, l'appel de lambdas non initialisés est un comportement non défini et entraînera généralement le plantage du programme. Par exemple, ne faites jamais ceci:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Exemples
Voici le code de ce que vous vouliez faire dans votre question avec 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;
}
Voici un exemple plus avancé d'un 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;
}
Non, ce n'est pas permis. Ni C ni C ++ ne prennent en charge cette fonctionnalité par défaut, cependant TonyK souligne (dans les commentaires) qu'il existe des extensions au compilateur GNU C qui permettent ce comportement en C.
Toutes ces astuces ressemblent (plus ou moins) à des fonctions locales, mais elles ne fonctionnent pas comme ça. Dans une fonction locale, vous pouvez utiliser des variables locales de ses super fonctions. C'est une sorte de semi-globaux. Aucune de ces astuces ne peut le faire. Le plus proche est l'astuce lambda de c ++ 0x, mais sa fermeture est liée au temps de définition, pas au temps d'utilisation.
Vous ne pouvez pas définir une fonction libre dans une autre en C ++.
Permettez-moi de poster ici une solution pour C ++ 03 que je considère la plus propre possible. *
#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;
}
(*) dans le monde C ++ utilisant des macros n'est jamais considéré comme propre.
Mais nous pouvons déclarer une fonction à l'intérieur de main ():
int main()
{
void a();
}
Bien que la syntaxe soit correcte, elle peut parfois conduire à "l'analyse la plus vexante":
#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());
}
=> pas de sortie de programme.
(Seul avertissement Clang après la compilation).