为啥将迭代器作为参数传递并在尾部位置递归时后缀失败并且前缀工作正常? [复制]

Posted

技术标签:

【中文标题】为啥将迭代器作为参数传递并在尾部位置递归时后缀失败并且前缀工作正常? [复制]【英文标题】:Why does postfix failed and prefix work fine when pass an iterator as argument and recurse it at tail position? [duplicate]为什么将迭代器作为参数传递并在尾部位置递归时后缀失败并且前缀工作正常? [复制] 【发布时间】:2017-10-10 16:31:40 【问题描述】:

我偶然遇到了这个问题。

本以为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)->autoa *= 2; );
    for (auto v : a)
    
        std::cout << v << std::endl;
    
    return 0;

1

2

3

4

按任意键继续。 . .


Howerer,如果我使用后缀,IteratorBegin 神经到达 iteratorEnd 并走得很远,所以段错误。

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)->autoa *= 2; );
    for (auto v : a)
    
        std::cout << v << std::endl;
    
    return 0;

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

分段错误(核心转储)

这是 Clang 的:

发生错误(超时)。请稍后再试。

【问题讨论】:

听起来您可能需要学习如何使用调试器来单步调试您的代码。使用好的调试器,您可以逐行执行您的程序,并查看它与您期望的偏差在哪里。如果您要进行任何编程,这是必不可少的工具。延伸阅读:How to debug small programs 如果postfix按照通常的语义来实现,应该很明显了。 以这种方式使用 switch 语句是不寻常的。我只是使用一个普通的旧 if/else。 @NathanOliver 我试过调试,比如step into function,然后发现IteratorBeginner不断增加。 @NathanOliver 谢谢!我很粗心,没有注意到迭代器本身仍然是同一个地址,不同的是它的值。可能是熬夜很多天的结果吧,lol。 【参考方案1】:

此声明

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

【讨论】:

【参考方案2】:

前缀大小写:

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

说,首先IteratorBegin加一,然后调用函数fun。之后,返回。

另一方面,后缀大小写:

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

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

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

【讨论】:

【参考方案3】:

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

【讨论】:

以上是关于为啥将迭代器作为参数传递并在尾部位置递归时后缀失败并且前缀工作正常? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

python-day5-生成器迭代器及递归调用

为啥 QVector 的迭代器使用前缀增加而后缀在内部减少?

C++:为啥我不能将一对迭代器传递给 regex_search?

数组名作为函数的参数属于啥传递为啥

Leetcode 494 蛮力递归解决方案将变量作为属性使用,但当我将其作为参数传递时不起作用

将成员函数作为参数传递/c++