Объявление и определение
Компилятор (compiler) читает файл сверху вниз. Когда он встречает вызов функции, ему уже нужно знать её имя, типы параметров (parameters) и тип результата. Поэтому функцию, определённую ниже main, вызвать из main не получится:
#include <iostream>
int main() {
std::cout << twice(21) << '\n'; // ✗
}
int twice(int x) {
return x * 2;
}error: use of undeclared identifier 'twice'Можно перенести twice выше main, и ошибка исчезнет. Но если функция определена в другом файле, переносить нечего — о ней нужно сообщить компилятору иначе.
Объявление
Объявление (declaration) сообщает компилятору имя функции, типы её параметров и тип результата. Тела у объявления нет — вместо него стоит точка с запятой. Такое объявление ещё называют прототипом.
int twice(int x);Имена параметров в объявлении можно не писать: int twice(int); значит то же самое. Компилятору нужны только типы, а имя x подсказывает читателю, что передаётся в функцию.
Определение (definition) — это объявление вместе с телом в фигурных скобках, поэтому функцию, определённую выше main, отдельно объявлять не нужно. Объявить функцию можно несколько раз, а определить — только один.
Вызов до определения
Объявление выше main решает задачу: вызов компилируется, а тело может стоять в конце файла.
#include <iostream>
int twice(int x);
int main() {
std::cout << twice(21) << '\n'; // 42
}
int twice(int x) {
return x * 2;
}Чтобы проверить вызов twice(21), компилятору хватает объявления: функция принимает int и возвращает int. Если тело лежит в другом .cpp-файле, вызов с ним свяжет компоновщик (linker). На этом основаны заголовочные файлы (header files): в них пишут объявления, а определения — в .cpp-файлах. Так устроен и #include <iostream>: он вставляет в файл объявления из стандартной библиотеки.
Частая ошибка
Функцию объявили, а определить забыли. Компилятор ошибки не видит, и сборка (build) останавливается на следующем шаге:
int twice(int x);
int main() {
return twice(21); // ✗
}undefined reference to `twice(int)'Это сообщение компоновщика, а не компилятора: объявления хватило, чтобы проверить вызов, но тела twice нет ни в одном файле программы. В задачах курса то же сообщение выглядит иначе: undefined symbol: twice(int).
Та же ошибка появится, если определение не совпадёт с объявлением по типам параметров. int twice(long x) — уже другая функция, и объявленная twice(int) снова останется без тела.