2009-05-30 2 views
7

Я был reading связанным вопросом, который заставляет меня задавать этот вопрос.Вложенные функции не допускаются, но почему вложенные прототипы функций разрешены? [C++]

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

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() будет иметь область действия только в main. Но я был неправ. Почему компилятор позволяет компилировать код, как указано выше? Есть ли ситуации в реальном времени, когда смысл кода, подобного приведенному выше, имеет смысл?

Любые мысли?

+0

+1 Просто нажмите на тот же запрос, и ответы ниже содержат всю информацию и многое другое. – slashmais

ответ

8

Ваш прототип всего лишь 'Forward Declaration'. Посмотрите статью в Википедии.

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

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

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

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

5

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

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

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

3

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

3

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

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

int main(int argc,char**argv) 
{ 
    string someString(); 
    string s = somestring(); // OK 
    ... 
} 
7

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

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

Ужасное злоупотребление языком. – Joshua

+0

Почему это «злоупотребление»? (Мне по-настоящему любопытно) – Samaursa

5

Относительно того, почему ваша декларация

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, сущность (сама функция) стала членом пространства имен ограждающей ,

+0

Хорошее объяснение. Много спасибо litb –

+1

+1 Это последнее «дружеское» - это странно - просто не могу доверять друзьям;) – slashmais

+0

Я не получаю трюк 'friend'. Делает ли 'X :: f()' принадлежать к охватывающему пространству имен? – ofavre

Смежные вопросы