为什么当将迭代器作为参数传递并在尾部位置递归时,后缀失败而前缀工作正常?
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
我无意中遇到了这个问题。
本以为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