假设我们需要遍历一个容器。传统的for循环如下所示:
for (auto it = container.begin(), end = container.end();
it != end;
++it)
{
doStuff(*it);
}
而基于范围的for如下所示:
for (auto& element : container)
{
doStuff(element);
}
现在,在开发的某个时刻,我们意识到出于某种原因,我们需要在这些循环迭代中增加其他内容。
需要增加的可能是各种各样的东西。例如,如果我们将相关数据存储在相同大小的其他容器中,那么在进行迭代时,我们可能也需要将迭代器增加到这些容器中(尽管我希望标准库的 future 版本能够使我们做更多的事情)通过结构化绑定(bind)和标准版本的boost::range::combine或其他方式来表达)。
在下面,为简单起见,我们假设要为每个元素分配一个ID,因此需要增加的只是一个计数器。
现在,传统循环看起来像这样:
unsigned int elementID = 0u;
for (auto it = container.begin(), end = container.end();
it != end;
++it, ++elementID)
{
doStuff(*it, elementID);
}
几乎不需要更改任何内容,并且在
++elementID
之后添加++it
可以保证无论每次迭代后计数器如何递增。即使另一个程序员修改了循环的主体,并说通过continue
在特定条件下提前进入下一个迭代,也不会冒着他们忘记增加计数器或类似内容的风险。现在,据我所知,使用基于范围的for,进行增量的唯一方法是执行以下操作:
unsigned int elementID = 0u;
for (auto& element : container)
{
doStuff(element, elementID);
++elementID;
}
也就是说,将增量放在循环体内。
就
elementID
而言,它的表现力较差(即,如果代码的主体很长,那么阅读该代码的人将不会一眼就看到我们也在迭代elementID
),并且也不能保证我上面提到的ve,因此也容易出错。真的没有其他方法可以实现基于范围的for吗?还是有一种方法可以按照我根本不知道的
for(auto& element : container; ++elementID){...}
编写东西?在人们回答之后进行编辑
内文建议使用boost的BOOST_SCOPE_EXIT_ALL,就非 native 解决方案而言,这与我所想到的最接近。
我不确定实际的实现方式,但是我猜这依赖于lambda和析构函数。我写这个来测试一下:
template <typename T>
class ScopeExitManager
{
public:
ScopeExitManager(T const& functionToRunOnExit) : _functionToRunOnExit(functionToRunOnExit)
{
}
~ScopeExitManager()
{
_functionToRunOnExit();
}
private:
T _functionToRunOnExit;
};
template <typename T>
ScopeExitManager<T> runOnScopeExit(T const& functionToRunOnExit)
{
return {functionToRunOnExit};
}
然后,我可以按照以下方式写点东西:
unsigned int elementID = 0u;
for (auto& element : container)
{
// Always at the beginning of the loop
auto scopeExitManager = runOnScopeExit([&elementID](){++elementID;});
// Actual body of the loop
doStuff(element, elementID);
}
这是表达性的,并保证
elementID
将增加。这很棒! 最佳答案
完成此操作的另一种方法是使用类似Boost.ScopeExit的方法,例如:
unsigned int elementID = 0u;
for (auto& element : container)
{
BOOST_SCOPE_EXIT_ALL(&) { ++elementID; };
doStuff(element, elementID);
}