Search code examples
c++recursionprefixpostfix-mta

Why does postfix failed and prefix work fine when pass an iterator as argument and recurse it at tail position?


I come across this problem accidentally.

I have thought google can solve it surely, but after searching multiple keywords, I still can't find answers, which confused me a lot.

When I use prefix at tail position, codes works fine:

template<class ContinerIterator, class F>
constexpr auto fun(ContinerIterator IteratorBegin, ContinerIterator IteratorEnd, F f)
{
    switch (IteratorBegin == IteratorEnd)
    {
    case true: return;
    case false: f(*IteratorBegin);
    }
    return fun(++IteratorBegin, IteratorEnd, f);
}
int main()
{
    std::vector<int> a = { 1, 2, 3, 4 };
    fun(std::begin(a), std::end(a), [](auto &a)->auto{a *= 2; });
    for (auto v : a)
    {
        std::cout << v << std::endl;
    }
    return 0;
}

1

2

3

4

Press any key to continue . . .


Howerer, if I use postfix, IteratorBegin nerve arrives iteratorEnd and goes far and far away, so segmentfault.

template<class ContinerIterator, class F>
constexpr auto fun(ContinerIterator IteratorBegin, ContinerIterator IteratorEnd, F f)
{
    switch (IteratorBegin == IteratorEnd)
    {
    case true: return;
    case false: f(*IteratorBegin);
    }
    return fun(IteratorBegin++, IteratorEnd, f);
}
void test()
{

}
int main()
{
    std::vector<int> a = { 1, 2, 3, 4 };
    fun(std::begin(a), std::end(a), [](auto &a)->auto{a *= 2; });
    for (auto v : a)
    {
        std::cout << v << std::endl;
    }
    return 0;
}

I have tried on MSVC, G++, Clang, all fails. Here is gcc's error list:

Segmentation fault (core dumped)

Here is Clang's:

Error occurred (timeout). Try again later.


Solution

  • This statement

    return fun(IteratorBegin++, IteratorEnd, f);
    

    with some exceptions can be considered like

    fun(IteratorBegin, IteratorEnd, f);
    ++IteratorBegin;
    return;
    

    So the function is always called with the same value of IteratorBegin.

    From the C++ Standard (5.2.6 Increment and decrement)

    1 The value of a postfix ++ expression is the value of its operand. [ Note: the value obtained is a copy of the original value —end note ]...

    Consider the following simple program

    #include <iostream>
    
    void f(int x)
    {
        std::cout << "Inside f( x ): x = " << x << std::endl;
    }
    
    int main()
    {
        int x = 0;
    
        std::cout << "Before f( x ): x = " << x << std::endl;
        f(x++);
        std::cout << "After  f( x ): x = " << x << std::endl;
    
        return 0;
    }
    

    Its output is

    Before f( x ): x = 0
    Inside f( x ): x = 0
    After  f( x ): x = 1
    

    Also it will be useful to consider the following simple program

    #include <iostream>
    
    int x = 0;
    
    void f(int x)
    {
        std::cout << "Local (parameter) x = " << x << std::endl;
        std::cout << "Global variable ::x = " << ::x << std::endl;
    }
    
    int main()
    {
        f(x++);
    
        return 0;
    }
    

    Its output is

    Local (parameter) x = 0
    Global variable ::x = 1