Почему я могу определить структуры и классы внутри функции в С++?

Я просто ошибочно сделал что-то подобное на С++, и он работает. Почему я могу это сделать?

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

    MyStruct s;
    s.somevalue = 5;
}

Теперь, после этого, я как бы вспомнил, как долгое время читал об этом трюке, как о некотором программном инструменте для слабого человека для С++, но я не могу вспомнить, почему это действительно, или где я прочитайте его.

Ответы на любой вопрос приветствуются!

Примечание. Хотя при написании вопроса я не получал ссылок на этот вопрос, текущая боковая панель указывает на это, поэтому я поставлю его здесь для справки, в любом случае вопрос другой, но может быть полезным.

Ответ 1

[EDIT 18/4/2013]: К счастью, ограничение, упомянутое ниже, было снято на С++ 11, поэтому локально определенные классы полезны в конце концов! Благодаря комментарию бамбука.

Возможность определять классы локально создавала бы более удобные функции для создания пользовательских функторов (классы с operator()(), например, функции сравнения для перехода к std::sort() или "тела цикла", которые будут использоваться с std::for_each()).

К сожалению, С++ запрещает использование локально определенных классов с шаблонами, поскольку у них нет связей. Поскольку большинство применений функторов связаны с типами шаблонов, которые шаблонизированы по типу функтора, локально определенные классы не могут быть использованы для этого - вы должны определить их вне функции.: (

[РЕДАКТИРОВАТЬ 1/11/2009]

Соответствующая цитата из стандарта:

14.3.1/2:. Локальный тип, тип без привязки, неназванный тип или тип, составленный из любого из этих типов, не должны использоваться в качестве аргумента шаблона для параметр типа шаблона.

Ответ 2

Одно приложение локально определенных классов С++ находится в 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;
}

Хотя вы можете сделать то же самое с анонимным пространством имен.

Ответ 3

Ну, в принципе, почему бы и нет? A struct в C (восходящий к рассвету времени) был всего лишь способом объявить структуру записи. Если вы хотите, почему бы не объявить его, где бы вы объявили простую переменную?

Как только вы это сделаете, помните, что цель С++ должна быть совместима с C, если это вообще возможно. Так оно и осталось.

Ответ 4

Это действительно очень полезно для выполнения некоторых работ по безопасности исключений на основе стека. Или общая очистка от функции с несколькими точками возврата. Это часто называют ИИОИ RAII (поиск ресурсов - инициация).

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

Он упомянул, например, раздел "7.8: Локальные классы: классы внутри функций" http://www.icce.rug.nl/documents/cplusplus/cplusplus07.html, который называет его "локальный класс" и говорит, что он "может быть очень полезен в расширенных приложениях, включая наследование или шаблоны".

Ответ 6

Это для создания массивов объектов, которые были правильно инициализированы.

У меня есть класс C, который не имеет конструктора по умолчанию. Я хочу массив объектов класса C. Я выясню, как я хочу, чтобы эти объекты были инициализированы, а затем вывести класс D из C со статическим методом, который предоставляет аргумент для конструктора по умолчанию C in 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;
}

Для простоты в этом примере используется тривиальный конструктор, отличный от стандартного, и случай, когда значения известны во время компиляции. Прямо расширять эту технику до тех случаев, когда требуется массив объектов, инициализированных значениями, которые известны только во время выполнения.