为什么当将迭代器作为参数传递并在尾部位置递归时,后缀失败而前缀工作正常?

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

我无意中遇到了这个问题。

本以为google一定能解决,但是搜索了多个关键字,还是找不到答案,让我很困惑。

当我在尾部位置使用前缀时,代码工作正常:

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 . . .


然而,如果我使用后缀,IteratorBegin 神经到达 iteratorEnd 并且走得很远,所以 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;
}

我试过MSVC、G++、Clang,都失败了。 这是 gcc 的错误列表:

Segmentation fault (core dumped)

这是 Clang 的:

Error occurred (timeout). Try again later.

前缀大小写:

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

表示,IteratorBegin加一,然后调用函数fun。之后,return.

另一方面,后缀大小写:

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

说,首先调用fun(),然后递增迭代器,然后return。

这意味着 fun() 总是使用非递增迭代器调用。

在尾调用中使用后缀自增时,递归调用得不到迭代器的自增值。它在应用增量之前获取迭代器的值。因此,递归是无限的。这会导致堆栈溢出。

这条语句

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

有一些例外可以被认为是

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

所以这个函数总是用IteratorBegin的相同值调用。

来自C++标准(5.2.6自增自减)

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 ]...

考虑以下简单程序

#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;
}

它的输出是

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

考虑以下简单程序也会很有用

#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;
}

它的输出是

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