Почему требуется прототип даже без объявления класса?

Если я просто сделаю это: Пример 1:

#include <iostream>

int main()
{
    //try to call doSomething function
    doSomething();
}

void doSomething()
{
    std::cout << "Call me now!" << std::endl;
}

Я получаю ошибку компиляции! Потому что компилятор не знает, что такое "doSomething".

Но если я изменю позицию doSomething на первое место, программа успешно скомпилируется. Пример 2:

#include <iostream>

void doSomething()
{
    std::cout << "Call me now!" << std::endl;
}

int main()
{
    //try to call doSomething function
    doSomething();
}

Я могу объявить прототип следующим образом: Ex3:

#include <iostream>

void doSomething(void);

int main()
{
    //try to call doSomething function
    doSomething();
}

void doSomething()
{
    std::cout << "Call me now!" << std::endl;
}

Но почему первый пример не работает? Почему я должен сначала объявлять прототип или вызывать функции, а затем основную функцию?

Спасибо!


person Ito    schedule 20.11.2011    source источник
comment
Это как: друг говорит вам флокс джокселей. Если вы никогда не слышали этого раньше, вы не сможете это сделать. Однако, если ваш друг ранее рассказал вам, что это значит и как это сделать, вы сможете это сделать. Кроме того, если он сказал вам, что объяснит это позже, вы сможете сделать это, когда он объяснит. Но вы не можете делать то, чего не знаете.   -  person Seth Carnegie    schedule 20.11.2011
comment
Обратите внимание, что void в списке аргументов функции не является обязательным для C++; запись void doSomething(); эквивалентна void doSomething(void);. В C (не знаю, относится ли это к C99) первое указывает на функцию, которая может принимать неопределенное количество аргументов, но в C++ это означает, что функция не принимает никаких аргументов.   -  person Praetorian    schedule 20.11.2011


Ответы (3)


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

person Ernest Friedman-Hill    schedule 20.11.2011
comment
@ Фил просто потому, что так оно и работает. Java намного новее, чем C++, и я думаю, что такое поведение является наследием того времени, когда был изобретен C++. Java умнее в этом только потому, что она научилась у своих родителей. - person Seth Carnegie; 20.11.2011
comment
Нет, вовсе нет! Java не может вызвать метод объекта, не зная, что тип объекта включает такой метод. Я полагаю, вы можете использовать reflection для вызова метода без этих знаний, но это совсем не одно и то же. Это может сделать Ruby, Python и другие подобные языки, но не Java. - person Ernest Friedman-Hill; 20.11.2011
comment
Это имеет смысл для меня. Но кажется, что С++ 11 все еще сохраняет это поведение. Я не вижу причины, по которой это поведение нельзя исправить - person ComfortablyNumb; 20.11.2011
comment
О, вы имеете в виду, что Java может вызывать метод, определение которого появляется позже в том же файле. Да, это потому, что компилятор Java может сделать несколько проходов по исходному файлу, в то время как C++ по определению делает это за один проход. В обоих случаях компилятор должен знать о методах, прежде чем он сможет их вызвать, однако в Java он выполняет немного больше предварительной работы, прежде чем приступить к фактической генерации кода. - person Ernest Friedman-Hill; 20.11.2011
comment
@ Фил, за этим может быть какая-то философская или техническая причина, было бы хорошо задать это как вопрос или выполнить поиск в Google. - person Seth Carnegie; 20.11.2011
comment
Моя интуиция подсказывает мне, что разработчикам компилятора C++ будет очень сложно это исправить. Язык предлагает множество возможностей для фундаментального расширения языка своими собственными типами, применения перегрузок, шаблонов и требует, чтобы компилятор выводил типы аргументов и т. д. Если вы вдруг захотите все это, не предоставляя прототипы для просмотра (или должен предположить, что могут быть дополнительные кандидаты, которые могут появиться позже, с которыми он должен бороться), вероятно, очень усложняет ситуацию. Возможно, добавление одного дополнительного прохода сработает... но я не достаточно квалифицирован, чтобы обдумывать это. - person Mordachai; 20.11.2011
comment
@ErnestFriedman-Hill, нет, java позволяет использовать класс без его импорта, если класс находится в том же пакете. - person ComfortablyNumb; 20.11.2011
comment
@Phil: все, что делает import, это сообщает компилятору, в каком пакете найти класс. Если import нет, компилятор автоматически ищет в том же пакете. import не читает файл так, как это делает #include в C. - person Ernest Friedman-Hill; 20.11.2011
comment
@Mordachai: Авторам компиляторов трудно исправить, потому что все их компиляторы уже спроектированы в модели с одной компиляцией, а не потому, что это по своей сути очень сложная проблема для решения. - person Puppy; 20.11.2011

Потому что компилятор не видел doSomething до его использования.

Либо вы должны его прототипировать, либо сначала определить, чтобы компилятор знал, как анализировать его использование.

person Mordachai    schedule 20.11.2011

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

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

class MyClass {
    void foo() {bar();}
    void bar() {}
};

Но вы не можете делать то, что вы указали в своем вопросе.

person IronMensan    schedule 20.11.2011