C ++: как найти max_element, используя boost :: range?

Я пытаюсь вернуть итератор для самого большого элемента в фильтруемом диапазоне. Вот что у меня так далеко:

#include <boost/lambda/lambda.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <vector>
#include <iostream>

using namespace boost::adaptors;
using namespace boost::lambda;
using namespace std;

int main ()
{
vector<double> x = {100, 150, 200, 110};
auto it = boost::max_element(x | indexed(0) | filtered(_1>100)); /* problem here */
cout << it.index() << endl;

return 0;
}

Я ожидал, что код распечатает индекс в векторе x, который имеет самый большой элемент (то есть 2), но, к сожалению, он не компилируется (Linux 64bit, GCC 4.7.2), проблема заключается в строке, указанной выше. Первая ошибка компиляции, которую я получаю от компилятора (среди прочего), заключается в следующем:

/boost/tuple/detail/tuple_basic.hpp:396:36: ошибка: назначение члена только для чтения ‘boost :: tuples :: cons :: head’

Есть идеи, что я делаю не так? Или как еще я могу достичь того, что пытаюсь сделать? Заранее спасибо!

РЕДАКТИРОВАТЬ:

Изменение проблемной строки на:

auto it = boost::max_element<boost::return_found>(x | sliced(1,4) |   filtered(boost::function<bool(double)>(_1>100)));

похоже, возвращает итератор к самому большому элементу. Однако есть ли способ проверить, что итератор находится в пределах диапазона? Сравнение его с boost :: end (x) дает мне ошибку. Единственное, о чем я могу думать, это вернуться

auto another_range = boost::max_element<boost::return_found_end>(x | sliced(1,4) |   filtered(boost::function<bool(double)>(_1>100)));

и проверьте, если boost :: empty (another_range). Это единственный вариант? Благодарю.

4

Решение

Конкретная ошибка, с которой вы столкнулись, появляется из-за того, что лямбды-бусты не копируются. Вот более простой способ получить то же сообщение:

auto f1 = _1 > 100;
auto f2 = f1;
f2 = f1; // same error

Если вы предоставляете функтор CopyAssignable для filtered, boost.phoenix (который вы должны использовать в любом случае, boost.lambda находится на пути к унынию в пользу феникса), рукописная структура или старый верующий std::bind2nd(std::greater<double>(), 100)эта строка компилируется с помощью clang ++:

bind2nd demo: http://liveworkspace.org/code/2xKZIf

демонстрация Phoenix: http://liveworkspace.org/code/18425g

С gcc он не работает из-за некоторой проверки boost.concept, которая, вероятно, является ошибкой, но это спорный вопрос, потому что результат filtered является boost::filtered_rangeчьи итераторы не имеют .index() функция-член.

РЕДАКТИРОВАТЬ в ответ на комментарий:
Сравнение итератора в Filter_range с итератором в исходном векторе не сработает. Тем не менее, так как вы использовали вектор, и так как он все еще доступен, вы можете сравнить адреса, так как ни indexed ни filtered делать копии

#include <vector>
#include <iostream>
#include <cassert>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/phoenix.hpp>

using namespace boost::adaptors;
using namespace boost::phoenix::placeholders;

int main ()
{
std::vector<double> x = {100, 150, 200, 110};
auto it = boost::max_element( x | indexed(0) | filtered(arg1 < 110) );
assert(&x[0] <= &*it && &*it < &x[0] + x.size());
std::cout << "Element " << *it << " is at index " << &*it - &x[0] << '\n';
}

демонстрация http://liveworkspace.org/code/1zBIJ9

Или, для более общего решения, вы можете преобразовать свой вектор в вектор пар (когда boost получит zip-адаптер, он может быть аккуратно заархивирован с помощью counting_range) и нести исходный индекс последовательности вместе со значением через все преобразования.

4

Другие решения

Других решений пока нет …