Pourquoi puis-je définir des structures et des classes dans une fonction en C ++?

Je me suis juste trompé en C ++, et ça marche. Pourquoi est-ce que je peux faire ça?

int main(int argc, char** argv) { struct MyStruct { int somevalue; }; MyStruct s; s.somevalue = 5; } 

Maintenant, après avoir fait cela, je me suis souvenu avoir lu quelque chose sur cette astuce, il y a longtemps, comme une sorte d’outil de functional programming pour les pauvres en C ++, mais je ne me souviens plus pourquoi.

Les réponses aux deux questions sont les bienvenues!

Note: Bien que lors de l’écriture de la question, je n’ai pas eu de référence à cette question , la barre latérale actuelle l’indique, donc je vais la mettre ici pour référence, de toute façon la question est différente mais peut être utile.

[EDIT 18/4/2013]: Heureusement, la ressortingction mentionnée ci-dessous a été levée en C ++ 11, donc les classes définies localement sont utiles après tout! Merci à commentateur Bamboon.

La possibilité de définir des classes localement rendrait la création de foncteurs personnalisés (classes avec un operator()() , par exemple des fonctions de comparaison pour passer à std::sort() ou des “organismes de boucle” à utiliser avec std::for_each() ) plus pratique.

Malheureusement, C ++ interdit l’utilisation de classes définies localement avec des modèles , car ils n’ont pas de lien. Comme la plupart des applications de foncteurs impliquent des types de modèles basés sur le type de foncteur, les classes définies localement ne peuvent pas être utilisées pour cela – vous devez les définir en dehors de la fonction. 🙁

[EDIT 1/11/2009]

La citation pertinente de la norme est la suivante:

14.3.1 / 2: Un type local, un type sans couplage, un type sans nom ou un type composé de l’un de ces types ne doit pas être utilisé comme argument de modèle pour un paramètre de type modèle.

Une application de classes C ++ définies localement est dans le modèle de conception Factory :

 // In some header class Base { public: virtual ~Base() {} virtual void DoStuff() = 0; }; Base* CreateBase( const Param& ); // in some .cpp file Base* CreateBase( const Params& p ) { struct Impl: Base { virtual void DoStuff() { ... } }; ... return new Impl; } 

Bien que vous puissiez faire la même chose avec un espace de noms anonyme.

Eh bien, fondamentalement, pourquoi pas? Une struct en C (remontant à la nuit des temps) n’était qu’un moyen de déclarer une structure d’enregistrement. Si vous en voulez un, pourquoi ne pas pouvoir le déclarer là où vous déclareriez une variable simple?

Une fois que vous faites cela, alors rappelez-vous qu’un objective de C ++ était d’être compatible avec C si possible. Donc c’est resté.

C’est en fait très utile pour faire du travail de sécurité basé sur des stacks. Ou nettoyage général d’une fonction avec plusieurs points de retour. Ceci est souvent appelé idiome RAII (acquisition de la ressource est l’initialisation).

 void function() { struct Cleaner { Cleaner() { // do some initialization code in here // maybe start some transaction, or acquire a mutex or something } ~Cleaner() { // do the associated cleanup // (commit your transaction, release your mutex, etc.) } }; Cleaner cleaner(); // Now do something really dangerous // But you know that even in the case of an uncaught exception, // ~Cleaner will be called. // Or alternatively, write some ill-advised code with multiple return points here. // No matter where you return from the function ~Cleaner will be called. } 

Il est mentionné, par exemple, dans la section “7.8: Classes locales: classes à l’intérieur des fonctions” de http://www.icce.rug.nl/documents/cplusplus/cplusplus07.html qui l’appelle “classe locale” et le dit ” peut être très utile dans les applications avancées impliquant l’inheritance ou les modèles “.

C’est pour créer des tableaux d’objects correctement initialisés.

J’ai une classe C qui n’a pas de constructeur par défaut. Je veux un tableau d’objects de classe C. Je détermine comment je veux que ces objects soient initialisés, puis dérive une classe D de C avec une méthode statique qui fournit l’argument du constructeur par défaut de C in D:

 #include  using namespace std; class C { public: C(int x) : mData(x) {} int method() { return mData; } // ... private: int mData; }; void f() { // Here I am in f. I need an array of 50 C objects starting with C(22) class D : public C { public: D() : C(D::clicker()) {} private: // I want my C objects to be initialized with consecutive // integers, starting at 22. static int clicker() { static int current = 22; return current++; } }; D array[50] ; // Now I will display the object in position 11 to verify it got initialized // with the right value. cout < < "This should be 33: --> " < < array[11].method() << endl; cout << "sizodf(C): " << sizeof(C) << endl; cout << "sizeof(D): " << sizeof(D) << endl; return; } int main(int, char **) { f(); return 0; } 

Dans un souci de simplicité, cet exemple utilise un constructeur non par défaut sortingvial et un cas où les valeurs sont connues au moment de la compilation. Il est simple d'étendre cette technique aux cas où vous voulez un tableau d'objects initialisé avec des valeurs connues uniquement à l'exécution.