Je veux dire quelque chose comme:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Non, C++ ne supporte pas ça.
Modifier: Cette réponse est ancienne. Pendant ce temps, C++ 11 a des lambda qui peuvent donner un résultat similaire - voir les réponses ci-dessous.
Cela dit, vous pouvez avoir des classes locales et des fonctions (non -static
ou static
), afin que vous puissiez obtenir ceci dans une certaine mesure, bien que ce soit un peu un problème:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Cependant, je remets en question la pratique. Tout le monde le sait (enfin, maintenant :)
), C++ ne supporte pas les fonctions locales, donc ils sont habitués à ne pas en avoir. Cependant, ils ne sont pas habitués à ce kludge. Je passerais pas mal de temps sur ce code pour m'assurer qu'il ne sert vraiment qu'à permettre les fonctions locales. Pas bon.
À toutes fins utiles, C++ le supporte via lambdas :1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Ici, f
est un objet lambda qui agit comme une fonction locale dans main
. Des captures peuvent être spécifiées pour permettre à la fonction d'accéder aux objets locaux.
Dans les coulisses, f
est un objet function (c'est-à-dire un objet d'un type fournissant une operator()
). Le type d'objet de fonction est créé par le compilateur basé sur le lambda.
1 depuis C++ 11
Les classes locales ont déjà été mentionnées, mais voici un moyen de les laisser 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 sur l'utilisation de ceci, c'est juste une astuce amusante (peut faire, mais imho 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 JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
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()
}
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();
}
Réponse plus récente: Les versions plus récentes de C++ permettent également à lambdas de le faire mieux/correctement. Voir les réponses plus haut sur la page.
Non, ce n'est pas permis. Ni C, ni C++ ne supportent cette fonctionnalité par défaut, cependant TonyK souligne (dans les commentaires) qu'il existe des extensions du compilateur GNU C qui permettent ce comportement en C.
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) respectez 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 éventuellement nécessaire de compiler du code avec une chaîne d'outils différente, je resterais à l'écart de telles extensions.
Je serais également prudent avec l'utilisation de fonctions imbriquées. Ils constituent une belle solution pour gérer la structure de blocs de code complexes, mais cohérents (dont les éléments ne sont pas destinés à un usage général/externe). Ils sont également très utiles pour contrôler la pollution de l’espace de nommage longues classes dans les langues parlées.)
Mais comme n'importe quoi, ils peuvent être exposés à des abus.
Il est regrettable que C/C++ ne prenne pas en charge de telles fonctionnalités en tant que norme. La plupart des variantes de Pascal et Ada (presque tous les langages basés sur ALGOL). Même avec JavaScript. Même chose avec les langues modernes comme Scala. Même chose avec des langages vénérables comme Erlang, LISP ou Python.
Et comme pour le C/C++, malheureusement, Java (avec lequel je gagne le plus possible 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 en 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 englobantes) représentant un domaine réel aussi simple que possible.
Les fonctions imbriquées aident à gérer la complexité "privée" au sein d'une fonction. En l'absence de ces installations, il convient d'éviter de propager cette complexité "privée" dans un modèle de classe.
Dans les logiciels (et dans toutes les disciplines de l’ingénierie), la modélisation est une question de compromis. Ainsi, dans la réalité, il y aura des exceptions justifiées à ces règles (ou plutôt à des directives). Procédez avec prudence, cependant.
Vous ne pouvez pas définir une fonction libre dans une autre en C++.
Vous ne pouvez pas avoir de fonctions locales en C++. Cependant, C++ 11 a lambdas . Les Lambda 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)>
est un lambda renvoyant une int
et prenant deux arguments, un std::string
et un float
. Le plus commun est 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, à l'aide de la syntaxe lambda(arguments)
.
Pour définir un lambda, utilisez la syntaxe [captures](arguments){code}
(il y a d'autres façons de le faire, mais je ne les mentionnerai pas ici). arguments
est le type d'argument utilisé par lambda, et code
est le code à exécuter lors de l'appel de lambda. Habituellement, vous mettez [=]
ou [&]
en tant que captures. [=]
signifie que vous capturez toutes les variables de la portée dans lesquelles la valeur est définie par valeur, ce qui signifie qu'elles conserveront la valeur qu'elles avaient lors de la déclaration de lambda. [&]
signifie que vous capturez toutes les variables de 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. Le simple fait de spécifier leur nom les capturera par valeur, en spécifiant leur nom avec un &
avant de les capturer par référence. Par exemple, [=, &foo]
capturera toutes les variables par valeur sauf foo
qui sera capturé par référence et [&, foo]
capturera toutes les variables par référence sauf foo
qui sera capturé par valeur. Vous pouvez également capturer uniquement des variables spécifiques. Par exemple, [&foo]
capturera foo
par référence et ne capturera aucune autre variable. Vous pouvez également ne capturer aucune variable 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 changer la valeur d'une variable qui a été capturée par valeur dans un lambda (les variables capturées par une valeur ont un type const
dans le 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é constitue un comportement indéfini et provoque généralement le blocage du programme. Par exemple, ne faites jamais ceci:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Exemples
Voici le code pour ce que vous vouliez faire dans votre question en utilisant 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é de 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;
}
Toutes ces astuces ressemblent (plus ou moins) à des fonctions locales, mais elles ne fonctionnent pas comme ça. Dans une fonction locale, vous pouvez utiliser les variables locales de ses super fonctions. C'est un peu des 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.
Me laisser poster ici une solution pour C++ 03 que je considère comme 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++, l'utilisation de macros n'est jamais considérée comme propre.
Mais nous pouvons déclarer une fonction dans main ():
int main()
{
void a();
}
Bien que la syntaxe soit correcte, elle peut parfois conduire à "l'analyse la plus frustrante":
#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.
(Avertissement Clang seulement après compilation).
Lorsque vous essayez d'implémenter une fonction dans un autre corps de fonction, vous devez obtenir cette error
en tant que définition illégale:
error C2601: 'a' : local function definitions are illegal
IntelliSense: expected a ';'
Alors n'essayez plus jamais.