Используется ли для объявлений локальных функций?

Большинство программистов на С++, подобных мне, в какой-то момент допустили следующую ошибку:

class C { /*...*/ };

int main() {
  C c();     // declares a function c taking no arguments returning a C,
             // not, as intended by most, an object c of type C initialized
             // using the default constructor.
  c.foo();   // compiler complains here.

  //...
}

Теперь, когда ошибка довольно очевидна, как только вы это знаете, мне было интересно, есть ли разумное использование для такого объявления локальной функции, за исключением того, что вы можете это сделать, тем более, что нет возможности определить такую ​​локальную функцию в том же блоке; вы должны определить его в другом месте.

Я думаю, что локальные классы в стиле Java - довольно приятная функция, которую я часто использую, особенно анонимный. Некоторые локальные классы С++ (которые могут иметь встроенные функции-члены) могут быть использованы. Но это выражение локальной функции без определения вещи кажется мне очень неудобным. Это просто C-наследство или есть какой-то более глубокий случай использования, о котором я не знаю?

Изменить для неверующих: C c() не является объявлением указателя функции.

Эта программа

int main()
{
  void g();
  cout << "Hello ";
  g();
  return 0;
}

void g()
{
  cout << "world." << endl;
}

выходы Hello world. Эта программа

void fun()
{
  cout << "world." << endl;
}

int main()
{
  void g();
  g = fun;
  cout << "Hello ";
  g();
  return 0;
}

не компилируется. gcc жалуется:

error: cannot convert 'void ()()' to 'void ()()' in assignment

Комео:

error: expression must be a modifiable lvalue

Ответ 1

Мне нужны объявления локальных функций в C, когда я хотел передать их в качестве аргументов для какой-либо другой функции. Я делаю это все время на других языках. Причина заключается в инкапсуляции реализации структур данных.

например. Я определяю некоторую структуру данных, например. дерево или график, и я не хочу раскрывать детали его внутренней реализации, например. потому что я могу изменить или изменить его. Поэтому я предоставляю функции доступа и мутатора для своих элементов и функцию обхода для итерации по элементам. Функция обхода имеет в качестве аргумента функцию, которая действует на элемент; задача функции обхода - выполнять функцию аргумента для каждого элемента и, возможно, каким-то образом агрегировать результаты. Теперь, когда я вызываю функцию обхода, функция аргумента обычно является некоторой специализированной операцией, которая зависит от локального состояния и поэтому должна определяться как локальная функция. Необходимость нажать эту функцию, поскольку переменные, которые содержат локальное состояние, снаружи, чтобы быть глобальными, или во внутренний класс, созданный специально для их удержания, является уродливым. Но это проблема с C и Java, а не с С++.

Ответ 2

Единственное, что я могу придумать, это уменьшить объем объявлений функций:

int main()
{
    void doSomething();
    doSomething();
    return 0;
}

void otherFunc()
{
    doSomething();  // ERROR, doSomething() not in scope
}

void doSomething()
{
    ...
}

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

Ответ 3

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

Ответ 4

Единственное разумное использование для него, которое я вижу, - это разрешить только одной функции в модуле компиляции знать о функции, определенной в другом модуле компиляции. Я думаю, что это будет несколько разумным использованием, но это единственное, о чем я могу думать, и я думаю, что это перебор.

Ответ 5

Как сказано в третьем фрагменте этого ответа, он может помочь с затенением области.

#include <stdio.h>

void c(); // Prototype of a function named ``c''

int main() {

    c(); // call the function

    { // additional scoppe needed for C, not for C++
        int c = 0; // shadow the c function with a c integer variable
        c++;
        {
            void c(); // hide the c integer variable back with the c function
            c();
        }
        ++c;
    } //!

    return 0;
}

void c() {
    printf("Called\n");
}

Я не думаю, что это использование, вероятно, будет полезно часто, и есть вероятность, что он не будет возникать в какой-либо хорошо разработанной программе.

Я все еще думаю, что это наиболее вероятная причина для этой функции, определяя функции в последнее время, поскольку переменные просто не звучат как правильно.

Ответ 6

Я иногда делаю это по той же причине, которую мы призываем объявлять переменные непосредственно перед их первым использованием (а не ранее), а именно для улучшения удобочитаемости. (Да, я понимаю, что для переменных это важнее, потому что это избавляет вас от необходимости проверять, используется ли переменная до того, как вы считаете ее первым использованием). Наличие прототипа (особенно если оно больше задействовано, чем просто c()), близкое к вызову функции, улучшает читаемость. Тот факт, что частный случай C c() вводит в заблуждение людей, является неудачным, но общая идея объявления функций локально имеет смысл.

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

Ответ 7

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

class C
{
  public:
    void foo() {}
};


int main()
{
    // declare function d, taking no arguments, returning fresh C
    C d();

    // instantiate class (leave parenthesis out)
    C c;
    c.foo();

    // call declared function
    C goo = d();

    return 0;
}

C d()
{
    C c;
    // ..
    return c;
}