> не понимаю то, как например сама функция может являться типом данных?
Ответ на этот вопрос зависит от вашего образования. Если еще не закончили школу, то можете пока не заморачиваться. В высшей математике вводится понятие функционального пространства, элементами которого являются уже не числа, а функции и с этими функциями оперируют по правилам, подобным тем, которые используют, скажем, для операций с действительными числами.
В программировании аналогичным образом приходится использовать функторы (это объекты, классы которых реализуют operator(), позволяющий использовать такой объект-функтор как функцию) и обычные сишные функции, которые используют как переменную. Примером может быть например функтор std::negate, который если его вызвать с произвольным типом, поддерживающим операцию унарного минуса, вернет результат, имеющий этот же самый тип и со значением аргумента, к которому была применена эта операция унарного минуса.
Т.е. до момента, когда функция будет вызвана уже с какими-то аргументами, с ней (и другими функциями) возможно манипулировать абстрактно, без учета аргументов и прочего, создавая, например, некоторые комбинации и последовательности применения. Смысл этих действий, конечно, заключается в том, что рано или поздно все это будет вызвано уже с конкретными значениями.
> Ведь перед объявлением функции мы указываем какого типа данных она будет
int Foo() {}
Перед объявлением функции указывают тип возвращаемого значения. Когда говорится, что функция Foo имеет тип int, это означает, что она возвращает результат такого типа, а не то, что символ Foo - целое число. Если же в программе встретится Foo, то он будет иметь тип "указателя на функцию возвращающую целое число и не принимающую аргументов", вот именно в этом значении у функции имеется тип. Который, еще раз подчеркиваю, отличается от типа возвращаемого значения
>Получается может быть так, что мы указываем тип данных другому типу данных ??
Нет. Про возвращаемое значение см. выше, а тип этой самой объявленной функции (указателя на нее, он же символ Foo) на языке Си будет выглядеть как: int (*)() // Вот эту запись можно условно считать типом Foo
int (*t1)(); // А t1 и t2 - это уже переменные, имеющие тип,
int (*t2)(); // соответствующий функции Foo
auto t3 = Foo; // И t3, кстати, тоже