Переработка цикла над контейнером STL для использования функциональных методов - PullRequest
8 голосов
/ 16 марта 2011

У меня есть std::vector указателей Person объектов, которые имеют функцию-член std::string getName() const. Используя алгоритмы STL, я хочу подсчитать все Person объекты в векторе, где getName() возвращает «Чад».

Поведение, просто повторяющее цикл, будет:

int num_chads = 0;
for(std::vector<Person *>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
    if((*it)->getName() == "Chad")
        ++num_chads;
}

Я хочу переработать это, чтобы он использовал все алгоритмы STL и функторы и т. Д. (Сделать его более функционально-ориентированным). Вот что мне нужно сделать:

const int num_chads = std::count_if(vec.begin(), vec.end(),
                                    std::bind1st(std::bind2nd(std::equal_to, mem_fun(Person::getName)), "Chad"));

Как вы, вероятно, можете сказать, это не работает. Во-первых, насколько я понимаю, вы не можете использовать bind1st / bind2nd для объектов binder1st / binder2nd, поскольку они специально предназначены для работы с std::binary_functions. Во-вторых, и, что гораздо важнее, я не думаю, что использую правильную технику. Я делаю хочу связать один из аргументов с "Чадом", но с аргументом итератора я на самом деле просто хочу преобразовать значение итератора в строку перед вызовом связанной версии equals_to.

Я думаю, что это можно сделать с помощью Boost, но возможно ли это с помощью только ядра C ++ 03 (т.е. без C ++ 0x lambas!)?

РЕДАКТИРОВАТЬ: Может кто-нибудь придумать пример, который не использует пользовательский предикат (то есть просто с помощью инструментов, предоставляемых в наборе инструментов std)?

РЕДАКТИРОВАТЬ: Хотя ответ Матье является ответом из учебника о том, как использовать функторы в алгоритмах STL, ответ Кубби пришел из подхода, который я искал (хотя Матье ответил до того, как я отредактировал вопрос, чтобы сделать его более конкретным, так что извинения !).

Ответы [ 3 ]

9 голосов
/ 16 марта 2011

Я всегда находил лямбды относительно нечитаемыми.Я предпочитаю писать явные типы:

struct Named
{
  Named(char const* ref): _ref(ref) {}
  bool operator()(Person* p) const { return p && p->getName() == _ref; }
  char const* _ref;
};

size_t const c = std::count_if(vec.begin(), vec.end(), Named("Chad"));

Хотя определение Named является «вне строки», правильно выбранное имя передает намерение и скрывает детали реализации.Лично я считаю, что это хорошо, потому что тогда я не отвлекаюсь на детали реализации и не пытаюсь выяснить, что происходит, путем обратного инжиниринга кода (как бы это ни было очевидно).

5 голосов
/ 16 марта 2011

Поскольку никто еще не опубликовал фактический код повышения, C ++ 98 с повышением:

ptrdiff_t num_chads = std::count_if(vec.begin(), vec.end(),
                      boost::bind(&Person::getName, _1) == "Chad");

тестовый запуск https://ideone.com/PaVJe

Что касается чистого C ++, я не думаю, что этовозможно без адаптера compose1, присутствующего в STL, но не в C ++ stdlib ...

и вот оно (с помощью реализации STL в GCC)

ptrdiff_t num_chads = std::count_if(vec.begin(), vec.end(),
                     __gnu_cxx::compose1(
                         std::bind2nd(std::equal_to<std::string>(), "Chad"),
                         std::mem_fun(&Person::getName)));

тестовый запуск: https://ideone.com/EqBS5

РЕДАКТИРОВАТЬ: исправлено с учетом Person*

1 голос
/ 16 марта 2011

Использование boost::bind, оно в некотором смысле превосходит существующие стандартные механизмы привязки. boost::bind полностью совместим с C ++ 03.

...