Почему я могу определить структуры и классы внутри функции в C ++? - PullRequest
78 голосов
/ 18 мая 2009

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

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

    MyStruct s;
    s.somevalue = 5;
}

Теперь, после этого, я как-то вспомнил, что когда-то давно читал об этой уловке как своего рода инструмент функционального программирования для бедного человека на C ++, но я не могу вспомнить, почему это допустимо, или где я прочитай это.

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

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

Ответы [ 6 ]

67 голосов
/ 18 мая 2009

[РЕДАКТИРОВАНИЕ 18/4/2013]: К счастью, упомянутое ниже ограничение было снято в C ++ 11, так что локально определенные классы полезны в конце концов! Спасибо комментатора Бэмбуна.

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

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

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

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

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

28 голосов
/ 18 мая 2009

Одно приложение локально определенных классов C ++ находится в Шаблон проектирования фабрики :


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

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

9 голосов
/ 18 мая 2009

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

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

5 голосов
/ 14 октября 2011

На самом деле это очень полезно для выполнения некоторых работ по безопасности исключений на основе стека. Или общая очистка от функции с несколькими точками возврата. Это часто называют идиомой 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 голосов
/ 18 мая 2009

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

2 голосов
/ 18 мая 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...