Pregunta ¿Por qué puedo definir estructuras y clases dentro de una función en C ++?


Simplemente cometí algo así en C ++, y funciona. ¿Por qué puedo hacer esto?

int main(int argc, char** argv) {
    struct MyStruct
    {
      int somevalue;
    };

    MyStruct s;
    s.somevalue = 5;
}

Ahora, después de hacer esto, recordé leer acerca de este truco en algún lugar, hace mucho tiempo, como una especie de herramienta de programación funcional para C ++, pero no puedo recordar por qué esto es válido o dónde lo leí.

¡Las respuestas a cualquiera de las preguntas son bienvenidas!

Nota: Aunque al escribir la pregunta no obtuve ninguna referencia a esta pregunta, la barra lateral actual lo señala así que lo pondré aquí para referencia, de cualquier manera la pregunta es diferente pero podría ser útil.


74
2018-05-18 02:43


origen


Respuestas:


[EDITAR 18/4/2013]: Afortunadamente, la restricción que se menciona a continuación se ha eliminado en C ++ 11, ¡así que las clases definidas localmente son útiles después de todo! Gracias a commenter bamboon.

La capacidad de definir clases localmente haría hacer crear funtores personalizados (clases con operator()(), p.ej. funciones de comparación para pasar a std::sort() o "cuerpos de bucle" para ser utilizado con std::for_each()) mucho más conveniente.

Desafortunadamente, C ++ prohíbe el uso de clases definidas localmente con plantillas, ya que no tienen ningún vínculo. Debido a que la mayoría de las aplicaciones de funtores involucran tipos de plantillas que son plantillas en el tipo de functor, las clases definidas localmente no se pueden usar para esto; debe definirlas fuera de la función. :(

[EDIT 1/11/2009]

La cita relevante del estándar es:

14.3.1 / 2: .Un tipo local, un tipo sin enlace, un tipo sin nombre o un tipo compuesto de cualquiera de estos tipos no se utilizará como un argumento de plantilla para un parámetro de tipo de plantilla.


65
2018-05-18 02:55



Una aplicación de clases C ++ definidas localmente está en Patrón de diseño de fábrica:


// 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;
}

Aunque puede hacer lo mismo con el espacio de nombres anónimo.


27
2018-05-18 03:15



Bueno, básicamente, ¿por qué no? UN struct en C (volviendo al comienzo de los tiempos) era solo una forma de declarar una estructura de registro. Si quieres uno, ¿por qué no poder declararlo donde declararías una variable simple?

Una vez que lo hagas, recuerda que un objetivo de C ++ era ser compatible con C si fuera posible. Así que se quedó.


9
2018-05-18 02:47



De hecho, es muy útil para hacer un trabajo de seguridad de excepciones basado en la pila. O limpieza general de una función con múltiples puntos de retorno. Esto a menudo se denomina modismo RAII (adquisición de recursos es initialzation).

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.
}

6
2017-10-14 01:44



Se menciona, por ejemplo, en la sección "7.8: Clases locales: clases dentro de las funciones" de http://www.icce.rug.nl/documents/cplusplus/cplusplus07.html que lo llama una "clase local" y dice que "puede ser muy útil en aplicaciones avanzadas que implican herencia o plantillas".


4
2018-05-18 02:51



Se trata de crear matrices de objetos que se hayan inicializado correctamente.

Tengo una clase C que no tiene un constructor predeterminado. Quiero una matriz de objetos de la clase C. Descubro cómo quiero que esos objetos se inicialicen, luego obtengo una clase D de C con un método estático que proporciona el argumento para la C en el constructor por defecto 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;
}

En aras de la simplicidad, este ejemplo utiliza un constructor trivial no predeterminado y un caso en el que los valores se conocen en tiempo de compilación. Es fácil extender esta técnica a los casos en los que desee una matriz de objetos inicializados con valores conocidos solo en tiempo de ejecución.


2
2018-05-18 03:43