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

StackOverflow https://stackoverflow.com/questions/1034606

Вопрос

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

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-legacy или есть какой-то более глубокий вариант использования, о котором я не знаю?

Редактировать для неверующих: 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;
}

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

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

комо:

error: expression must be a modifiable lvalue
Это было полезно?

Решение

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

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

Другие советы

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

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

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

void doSomething()
{
    ...
}

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

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

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

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

#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");
}

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

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

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

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

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

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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top