基于范围的for和其他增量

Ete*_*nal 4 c++ for-loop c++11

假设我们需要遍历一个容器。传统的for循环如下所示:

for (auto it = container.begin(), end = container.end();
     it != end; 
     ++it)
{
    doStuff(*it);
}
Run Code Online (Sandbox Code Playgroud)

而基于范围的for如下所示:

for (auto& element : container)
{
    doStuff(element);
}
Run Code Online (Sandbox Code Playgroud)

现在,在开发的某个时刻,我们意识到由于某种原因,我们需要在这些循环迭代中增加其他内容。

需要增加的可能是各种各样的东西。例如,如果我们将相关数据存储在相同大小的其他容器中,那么在进行迭代时,我们可能也需要将迭代器增加到这些容器中(尽管我希望标准库的未来版本能够使我们做更多的事情)通过结构化绑定和标准版本的boost :: range :: combine或其他方式来表达。

在下面,为简单起见,我们假设要为每个元素分配一个ID,因此需要增加的只是一个计数器。


现在,传统循环看起来像这样:

unsigned int elementID = 0u;
for (auto it = container.begin(), end = container.end();
     it != end;
     ++it, ++elementID)
{
    doStuff(*it, elementID);
}
Run Code Online (Sandbox Code Playgroud)

几乎任何东西都必须更改,并在++elementID后面加上,以++it确保无论每次迭代后计数器如何递增计数器。即使另一个程序员修改了循环的主体,并说在某些条件下提前进入下一个迭代continue,也不会冒着他们忘记增加计数器之类的风险。


现在,据我所知,使用基于范围的for进行增量的唯一方法是执行以下操作:

unsigned int elementID = 0u;
for (auto& element : container)
{
    doStuff(element, elementID);
    ++elementID;
}
Run Code Online (Sandbox Code Playgroud)

也就是说,将增量放入循环体内。

这方面的表现力较差elementID(例如,如果代码的主体很长,那么正在阅读代码的人也不会一眼看到我们也在迭代elementID),并且也无法保证我已经提到以上,因此也容易出错。

真的没有其他方法可以实现基于范围的for吗?还是有办法按照for(auto& element : container; ++elementID){...}我根本不知道的方式写东西?


人们回答后进行编辑

内文建议使用boost的BOOST_SCOPE_EXIT_ALL,这与我对非本机解决方案的想法最为接近。

我不确定实际的实现方式,但是我想这依赖于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};
}
Run Code Online (Sandbox Code Playgroud)

然后,我可以按照以下方式写点东西:

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);
}
Run Code Online (Sandbox Code Playgroud)

这是表达性的,并保证elementID会增加。这很棒!

Nev*_*vin 5

完成此操作的另一种方法是使用Boost.ScopeExit之类的东西,如下所示:

unsigned int elementID = 0u;
for (auto& element : container)
{
    BOOST_SCOPE_EXIT_ALL(&) { ++elementID; };
    doStuff(element, elementID);
}
Run Code Online (Sandbox Code Playgroud)