It's because you misunderstand what "defining" a function means.
First of all let's get the semantics correct: In order to refer to a function you just need its declaration and not its complete implementation.
Typically, there are two steps in creating an executable: 1) Compiling and 2) Linking.
Compiling
The compiling step works out that the syntax of each piece is okay for each function. In order to compile you need the core language keywords or properly declared symbols. The compiler puts these together but does not resolve whether or not those symbols actually point to real objects in the executable. An object is either bits of data, or bits of instruction. Usually these blobs are called data-segments and text-segments in an object file.
Linking
It's in the linking step that each of the blobs, which is still identified using symbols is put together to see if all the references in the code have corresponding blobs.
Self-reference
Now that you know that all you need to evaluate whether or not syntax is correct (compilation step) is the declaration. The fact that you have a symbol reference within the body of the function itself is not a problem at all.
What you are doing above is short-hand for the following:
int function(int &m); // implicit declaration
int function(int &m) {
m = 2*m;
if(m < 20)
{
function(m); // this only requires the declaration to be accepted
// by the compiler.
}
return m;
};
int main() {
int a = 2;
std::cout <<"Now a = "<<function(a);
return 1;
}
This ability is very important because it's fundamental to creating recursive functions. Some problems can be solved with recursion a lot more elegantly than with iterations, especially when you start throwing in n-way recursive functions.