if(*i > x && *i < y)) break; // элемента или достижения v.end
}
… // После завершения цикла
// i указывает на искомый элемент
// или совпадает с v.end
То же самое можно сделать и при помощи find_if
, но для этого придется воспользоваться нестандартным адаптером объекта функции — например, compose2
из реализации SGI (см. совет 50):
vector
find_if(v.begin, v.end, // Найти первое значение
compose2(logical_and
bind2nd(greater
bind2nd(less
Но даже если бы нестандартные компоненты не использовались, многие программисты полагают, что вызов алгоритма значительно уступает циклу по наглядности, и я склонен с ними согласиться (см. совет 47).
Вызов find_if
можно было бы упростить за счет выделения логики проверки в отдельный класс функтора.
template
class BetweenValues:
public unary_function
public:
BetweenValues(const T& lowValue, const T& highValue) :
lowVal(lowValue), highVal(highValue) {}
bool operator(const T& val) const {
return val > lowVal && val < highVal;
}
private:
T lowVal;
T highVal;
};
…
vector
BetweenValues
Однако у такого решения имеются свои недостатки. Во-первых, создание шаблона BetweenValues
требует значительно большей работы, чем простое написание тела цикла. Достаточно посчитать строки в программе: тело цикла — одна строка, BetweenValues
— четырнадцать строк. Соотношение явно не в пользу алгоритма. Во-вторых, описание критерия поиска физически отделяется от вызова. Чтобы понять смысл вызова find_if
, необходимо найти определение BetweenValues
, но оно должно располагаться вне функции, содержащей вызов find_if
. Попытка объявить BetweenValues
find_if
:
{ // Начало функции
…
template
class BetweenValues: public unary_function
vector
BetweenValues
} // Конец функции
не компилируется, поскольку шаблоны не могут объявляться внутри функций. Если попробовать обойти это ограничение посредством реализации BetweenValues
в виде класса:
{ // Начало функции
…
class BetweenValues: public unary_function
vector
BetweenValues(x, y));
} // Конец функции
все равно ничего не получается, поскольку классы, определяемые внутри функций, являются find_if
). Печально, но классы функторов и шаблоны классов функторов не разрешается определять внутри функций, как бы удобно это ни было.
В контексте борьбы между вызовами алгоритмов и циклами это означает, что выбор определяется исключительно содержимым цикла. Если алгоритм уже умеет делать то, что требуется, или нечто очень близкое, вызов алгоритма более нагляден. Если задача элементарно решается в цикле, а при использовании алгоритма требует сложных нагромождений адаптеров или определения отдельного класса функтора, вероятно, лучше ограничиться циклом. Наконец, если в цикле приходится выполнять очень длинные и сложные операции, выбор снова склоняется в пользу алгоритмов, потому что длинные и сложные операции лучше оформлять в отдельных функциях. После того как тело цикла будет перенесено в отдельную функцию, почти всегда удается передать эту функцию алгоритму (особенно часто — алгоритму for_each
) так, чтобы полученный код был более наглядным и прямолинейным.