web-dev-qa-db-fra.com

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

J'ai juste fait par erreur quelque chose comme ça 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 d'avoir lu cette astuce quelque part, il y a longtemps, comme une sorte d'outil de programmation fonctionnelle du pauvre homme pour C++, mais je ne me souviens pas pourquoi cela est valide, ni où je l'ai lu.

Les réponses à l'une ou l'autre question sont les bienvenues!

Remarque: Bien que lors de la rédaction de la question, je n'ai reçu aucune référence à cette question , la barre latérale actuelle le signale donc je vais le mettre ici pour référence, de toute façon la question est différente mais pourrait être utile.

80
Robert Gould

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

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

Malheureusement, C++ interdit d'utiliser des classes définies localement avec des modèles, car ils n'ont pas de lien. Étant donné que 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:

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

67
j_random_hacker

Une application des classes C++ définies localement se trouve dans modèle de conception d'usine :


// 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 de même avec un espace de noms anonyme.

28
Nikolai Fetissov

Eh bien, pourquoi pas? Un 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 simple variable?

Une fois que vous avez fait cela, souvenez-vous qu'un objectif de C++ était d'être compatible avec C si possible. Alors ça est resté.

9
Charlie Martin

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 un " classe locale "et dit qu'elle" peut être très utile dans les applications avancées impliquant l'héritage ou les modèles ".

5
ChrisW

Il est en fait très utile pour effectuer un travail de sécurité des exceptions basé sur la pile. Ou nettoyage général d'une fonction avec plusieurs points de retour. Ceci est souvent appelé l'idiome RAII (l'acquisition de ressources 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.
}
5
simong

C'est pour faire des tableaux d'objets correctement initialisés.

J'ai une classe C qui n'a pas de constructeur par défaut. Je veux un tableau d'objets de classe C. Je comprends comment je veux que ces objets soient initialisés, puis dérive une classe D de C avec une méthode statique qui fournit l'argument pour le C dans le constructeur par défaut de D:

#include <iostream>
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;
}

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

3
Thomas L Holaday