Вложенные функции не разрешены, но почему разрешены прототипы вложенных функций?[C++]

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

Вопрос

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

Рассмотрим следующий код

int main()
{
    string SomeString();
}

Все говорит о том, что компилятор воспринимает это как прототип функции, а не как строка объект.Теперь рассмотрим следующий код.

int main()
{
    string Some()
    {
        return "";
    }
}

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

Я выяснил, что допустимо следующее.

int main()
{ 
  string SomeFun();
  SomeFun();
  return 0;
}

string SomeFun()
{
  std::cout << "WOW this is unexpected" << std::endl;
}

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

Есть какие-нибудь мысли?

Это было полезно?

Решение

Ваш прототип - это просто 'Прямое заявление'.Пожалуйста, ознакомьтесь со статьей в Википедии.

По сути, это сообщает компилятору "не пугайтесь, если ярлык "SomeFun" будет использоваться таким образом". Но ваш компоновщик - это то, что отвечает за поиск правильного тела функции.

На самом деле вы можете объявить поддельный прототип, например'char SomeFun()' и используйте его по всему вашему основному.Вы получите сообщение об ошибке только тогда, когда ваш компоновщик попытается найти тело вашей поддельной функции.Но ваш компилятор отнесется к этому спокойно.

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

Надеюсь, это поможет.

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

Просто в качестве дополнительного примечания, в C ++ 03 действительно есть обходной способ определения локальных функций.Это требует злоупотребления функцией локального класса:

int main()
{
    struct Local
    {
        static string Some()
        {
            return "";
        }
    };
    std::cout << Local::Some() << std::endl;
}

Это соглашение из C - как и многие другие, - которое принял C ++.

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

Если вы хотели бы иметь функции, которые существуют только в области действия другой функции, есть два варианта повышение::лямбда и C ++1x лямбда-код.

Относительно того, почему ваше заявление о

void f() {
    void g(); g();
}

это лучше, чем этот

void g();
void f() {
    g();
}

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

void f() {
    int g; 
    // oops, ::g is shadowed. But we can work around that
    {
        void g(); g();
    }
}

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

int main() {
    using std::exit;
    exit();
}

В качестве дополнительного примечания, существует больше подобных ситуаций, когда целевой областью объявления является нет область, в которой появляется это объявление.Как правило, объект, который вы объявляете, становится членом области, в которой появляется объявление.Но это не всегда так.Рассмотрим, например, объявления друзей, где это происходит

struct X { friend void f() { std::cout << "WoW"; } };
int main() { void f(); f(); } // works!

Даже несмотря на объявление функции (и определение!) f произошло в рамках X, сущность (сама функция) стала членом окружающего пространства имен.

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

Когда вы объявляете прототип, как вы это делаете, вы, по сути, приказываете компилятору подождать, пока компоновщик разрешит его.В зависимости от того, где вы пишете прототип, применяются правила определения области действия.Нет ничего технически неправильного в написании прототипа внутри вашей функции main () (хотя, ИМХО, это немного запутаннее), это просто означает, что функция известна только локально внутри main().Если бы вы объявили прототип в верхней части вашего исходного файла (или, чаще, в заголовочном файле), прототип / функция были бы известны во всем исходном коде.

string foo()
{
  string ret = someString();  // Error
  return ret; 
}

int main(int argc,char**argv)
{
   string someString();
   string s = somestring(); // OK
   ...
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top