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

问题描述:

我偶然发现了这个问题。为什么在传递迭代器作为参数并递归到尾部位置时,postfix失败并且前缀工作正常?

我以为谷歌可以肯定地解决它,但在搜索多个关键字后,我仍然找不到答案,这使我困惑了很多。

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

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

按任意键继续。 。 。


Howerer,如果我用postfix,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的错误列表:

分割故障(核心转储)

这里是锵的:发生

错误(超时)。稍后再试。

+2

这听起来像你可能需要学习如何使用调试器来逐步执行代码。使用一个好的调试器,您可以逐行执行您的程序,并查看它与您期望的偏离的位置。如果你打算做任何编程,这是一个重要的工具。进一步阅读:[如何调试小程序](http://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – NathanOliver

+0

如果postfix是按照通常的语义实现的,它应该是明显。 – StoryTeller

+0

用这种方式看到switch语句是很少见的。我只是使用一个普通的旧的if/else。 – templatetypedef

本声明

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

有一些例外可以考虑像

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

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

从C++标准(5.2.6递增和递减)

1后缀++表达式的值是它的操作数的值。 [ 注:得到的值是原始值的副本末端注 ] ...

考虑下面的简单程序

#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 

前缀情况:

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

说,第一增量IteratorBegin接一个,然后调用函数fun。之后,返回。

。另一方面,后缀的情况:

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

说,第一个电话fun(),然后递增迭代器,然后返回。

这意味着fun()始终使用未递增的迭代器进行调用。

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