Tail recursion is a special case of tail call. The tail of the call is where the compiler can see that there are no operations that need to be done when returning from the called function - essentially turning the called function into its own. The compiler can often perform several stack fix operations and then go (instead of calling) to the address of the first instruction of the called function .
One of the great things besides eliminating some callbacks is that you also reduce stack usage. On some platforms or in OS code, stacks can be very limited, and on modern computers, such as the x86 processors on our desktop computers, reduces stack usage, which will improve the performance of the data cache.
Tail recursion is where the called function coincides with the calling function. This can be turned into loops, which is exactly the same as the leap in tail call optimization mentioned above. Since this is the same function (called and calling), there are fewer stack fixes that need to be done before the jump.
The following is a general way to make a recursive call, which will be more difficult for the compiler to turn into a loop:
int sum(int a[], unsigned len) { if (len==0) { return 0; } return a[0] + sum(a+1,len-1); }
It is simple enough that many compilers may still be able to figure it out, but as you can see, there is an addition that should happen after the number returns from the called sum, so simple tail call optimization is not possible.
If you have done this:
static int sum_helper(int acc, unsigned len, int a[]) { if (len == 0) { return acc; } return sum_helper(acc+a[0], len-1, a+1); } int sum(int a[], unsigned len) { return sum_helper(0, len, a); }
You could use calls in both functions that are tail calls. Here the main task of the sum function is to move the value and clear the position of the register or stack. Sum_helper does all the math.
Since you mentioned C ++ in your question, I will talk about some special things. C ++ hides from you some things that are not there. Of these destructors, the main thing that will interfere with tail call optimization.
int boo(yin * x, yang *y) { dharma z = x->foo() + y->bar(); return z.baz(); }
In this example, the baz call is not really a tail call, since z should be destroyed after returning from baz. I believe that C ++ rules can make it difficult to optimize even in cases where the variable is not required during the whole time of the call, for example:
int boo(yin * x, yang *y) { dharma z = x->foo() + y->bar(); int u = z.baz(); return qwerty(u); }
z can be destroyed after returning from qwerty here.
Another thing is implicit type conversion, which can also happen in C, but can be more complex and common in C ++. For example:
static double sum_helper(double acc, unsigned len, double a[]) { if (len == 0) { return acc; } return sum_helper(acc+a[0], len-1, a+1); } int sum(double a[], unsigned len) { return sum_helper(0.0, len, a); }
Here the sum_helper call is not a tail call, because sum_helper returns double, and the sum will need to convert it to int.
In C ++, references to objects that can have different kinds of interpretations, each of which may be a different type conversion, are returned quite often, For example:
bool write_it(int it) { return cout << it; }
There is a call here made for cout.operator <as the last statement. cout will return a link to itself (so you can insert many things together into a list separated by a <<symbol), which is then forcibly evaluated as bool, which ends with a call to another cout method, the BOOL () operator. This cout.operator bool () can be called as a tail call in this case, but the <<operator could not.
EDIT:
One thing worth mentioning is that the main reason for optimizing tail optimization in C is that the compiler knows that the called function will save this return value in the same place as the calling function, the return value is saved.