Допустим, нам нужно перебрать контейнер.Традиционный цикл for будет выглядеть следующим образом:
for (auto it = container.begin(), end = container.end();
it != end;
++it)
{
doStuff(*it);
}
, тогда как основанный на диапазоне for будет выглядеть так:
for (auto& element : container)
{
doStuff(element);
}
Теперь, в какой-то момент разработки мыПоймите, что по тем или иным причинам нам нужно увеличить что-то еще за эти итерации цикла.
То, что нужно увеличивать, может быть различным.Например, если у нас есть соответствующие данные, хранящиеся в других контейнерах того же размера, нам может понадобиться увеличивать итераторы в этих контейнерах также по мере прохождения итераций (хотя я надеюсь, что будущая версия стандартной библиотеки позволит нам сделать это большевыразительно, через структурированные привязки и стандартную версию boost :: range :: Объединение или что-то), то, что нужно увеличить, это просто счетчик.
Традиционный цикл теперь будет выглядеть так:
unsigned int elementID = 0u;
for (auto it = container.begin(), end = container.end();
it != end;
++it, ++elementID)
{
doStuff(*it, elementID);
}
Едва ли что-либо должно быть изменено, и добавление ++elementID
после ++it
гарантирует, что счетчик будет увеличиваться независимо от того, что после каждой итерации.Даже если бы другой программист изменил тело цикла и, скажем, рано переходил к следующей итерации при определенных условиях через continue
, не было бы никакого риска, что он забыл бы увеличить счетчик или что-то в этом роде.
Теперь, на основе диапазона, насколько я могу судить, единственный способ сделать приращение - сделать что-то вроде этого:
unsigned int elementID = 0u;
for (auto& element : container)
{
doStuff(element, elementID);
++elementID;
}
То есть, чтобы поместить приращение в тело цикла.
Это менее выразительно по отношению к elementID
(то есть, если тело кода длинное, кто-то, читающий код, не сразу увидит, чтомы тоже перебираем elementID
), и это не дает гарантии, о которой я говорил выше, поэтому оно также подвержено ошибкам.
Нет ли другого способа реализовать это с помощьюдиапазон на основе?Или есть способ написать что-то вроде for(auto& element : container; ++elementID){...}
, о котором я просто не знаю?
Редактировать после того, как люди ответили
Невинпредложенный Boost BOOST_SCOPE_EXIT_ALL, который наиболее близок к тому, что я имел в виду, когда речь идет о неродных решениях.
Я не уверен насчет фактической реализации, но я полагаю, что это зависит от лямбд и деструкторов.Я написал это, чтобы проверить это:
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
будет увеличен,Это здорово!