Вход/Регистрация
Стандарты программирования на С++. 101 правило и рекомендация
вернуться

Александреску Андрей

Шрифт:

Подумайте об использовании лямбда-функций [Boost]. Лямбда-функции представляют собой важный инструмент, который позволяет справиться с основным недостатком алгоритмов, а именно с удобочитаемостью. Без их применения вы должны использовать либо функциональные объекты (но тогда тела даже простых циклов находятся в отдельном месте, далеко от точки вызова), либо стандартные связыватели и функциональные объекты наподобие

bind2nd
и
plus
(достаточно запутанные, сложные и утомительные в использовании).

Примеры

Вот два примера, адаптированных из [Meyers01].

Пример 1. Преобразование

deque
. После того как было выполнено несколько некорректных итераций из-за недействительных итераторов (например, см. рекомендацию 83), мы пришли к окончательной версии цикла для прибавления 41 к каждому элементу массива данных типа
doublе
и помещения результата в дек
deque<doublе>
:

deque<double>::iterator current = d.begin;

for (size_t i =0; i < max; ++i) {

 // Сохраняем current действительным

 current = d.insert(current, data[i] + 41);

 ++current; // Увеличиваем его, когда это

} // становится безопасным

Вызов алгоритма позволяет легко обойти все ловушки в этом коде:

transform(

 data.begin, data.end, // Копируем элементы

 data inserter(d, d.begin), // в d с начала контейнера,

 bind2nd(plus<double>,41)); // добавляя к каждому 41

Впрочем,

bind2nd
и
plus
достаточно неудобны. Откровенно говоря, в действительности их мало кто использует, и связано это в первую очередь с плохой удобочитаемостью такого кода (см. рекомендацию 6).

При использовании лямбда-функций, генерирующих для нас функциональные объекты, мы можем написать совсем простой код:

transform(data, data+max, inserter(d,d.begin), _1 + 41);

Пример 2. Найти первый элемент между

x
и
у
. Рассмотрим простой цикл, который выполняет поиск в
vector<int> v
первого элемента, значение которого находится между
x
и
y
. Он вычисляет итератор, который указывает либо на найденный элемент, либо на
v.end
:

vector<int>::iterator i = v.begin;

for (; i != v.end; ++i)

 if (*i > x && *i < y) break;

Вызов алгоритма достаточно проблематичен. При отсутствии лямбда-функций у нас есть два варианта — написание собственного функционального объекта или использование стандартных связывателей. Увы, в последнем случае мы не можем обойтись только стандартными связывателями и должны использовать нестандартный (хотя и достаточно распространенный) адаптер

compose2
, но даже в этом случае код получается совершенно непонятным, так что такой код на практике никто просто не напишет:

vector<int>::iterator i =

 find_if(v.begin, v.end,

 compose2(logical_and<bool>,

 bind2nd(greater<int>, x), bind2nd(less<int>, y)));

Другой вариант, а именно — написание собственного функционального объекта — достаточно жизнеспособен. Он достаточно хорошо выглядит в точке вызова, а главный его недостаток— необходимость написания функционального объекта

BetweenValues
, который визуально удаляет логику из точки вызова:

template<typename T>

class BetweenValues : public unary_function<T, bool> {

public:

 BetweenValues(const T& low, const T& high)

: low_(low), high_(high) { }

 bool operator(const T& val) const

{ return val > low_ && val < high_; }

private:

 T low_, high_;

};

  • Читать дальше
  • 1
  • ...
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: