Использование адаптеров Boost с лямбдами C++11

Я пытался скомпилировать этот код:

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

int main() {
    std::vector<int> v{
        1,5,4,2,8,5,3,7,9
    };
    std::cout << *boost::min_element(v | boost::adaptors::transformed(
            [](int i) { return -i; })) << std::endl;
    return 0;
}

Сбой компиляции со следующим сообщением об ошибке (после длинного романа создания шаблона):

/usr/local/include/boost/iterator/transform_iterator.hpp:84:26: error: use of deleted function ‘main()::<lambda(int)>::<lambda>()’
../main.cpp:12:5: error: a lambda closure type has a deleted default constructor

Я нашел проблему в Google и нашел ее в архиве списка рассылки Boost Users. Предполагается, что с помощью #define BOOST_RESULT_OF_USE_DECLTYPE решит проблему. Я положил его в самом начале моего кода, но он все еще не компилируется. Длина сообщения об ошибке кажется намного короче, но сообщение об ошибке в конце остается тем же. Я в настоящее время использую Boost 1.50.

В чем здесь может быть проблема? Есть ли способ сделать эту работу?

5 ответов

Решение

http://smellegantcode.wordpress.com/2011/10/31/linq-to-c-or-something-much-better/

Но вы можете использовать это, это работает хорошо.

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

int main() {
    std::vector<int> v{
        1,5,4,2,8,5,3,7,9
    };
    std::function<int(int)> func = [](int i) { return -i; };
    std::cout << *boost::min_element(v | boost::adaptors::transformed(
    func)) << std::endl;
    return 0;
}

http://liveworkspace.org/code/b78b3f7d05049515ac207e0c12054c70

#define BOOST_RESULT_OF_USE_DECLTYPE прекрасно работает в VS2012, например.

Вы можете превратить необъявленную лямбду в указатель на функцию, поставив перед ней знак "+".

std::vector<int> v{1,5,4,2,8,5,3,7,9};
std::cout << *boost::min_element(v | 
    boost::adaptors::transformed(+[](int i) 
    {
        return -i; 
    })) << std::endl;

Это описано в http://boost.2283326.n4.nabble.com/range-cannot-use-lambda-predicate-in-adaptor-with-certain-algorithms-td3560157.html и https://svn.boost.org/trac/boost/ticket/4189 - проблема в том, что некоторые алгоритмы ожидают, что смогут копировать-конструировать (и конструировать по умолчанию, и копировать-назначать) свой предикат, чего нельзя сделать с помощью лямбды.

Обходной путь должен обернуть лямбду в std::function:

*boost::min_element(
    v | boost::adaptors::transformed(std::function<int(int)>(
        [](int i) { return -i; })));

Я попросил (в Inferring подпись вызова лямбды или произвольного вызова для "make_function") для способа написать make_function такой, что можно просто написать:

*boost::min_element(
    v | boost::adaptors::transformed(make_function(
        [](int i) { return -i; })));

С выводом аргументов шаблона класса объектов C++17 вы можете упростить перенос с помощью std::function, например:

*boost::min_element(
    v | boost::adaptors::transformed(std::function(
        [](int i) { return -i; })));

Как уже упоминали другие, проблема в том, чтоstd::min_element()(который просто обернутboost::min_element()) хочет скопировать начальный итератор диапазона. Но этот итератор содержит экземпляр вызываемого объекта преобразования (то же самое происходит и с вызываемым объектом фильтрации), который, следовательно, также необходимо скопировать. Но лямбды не копируются, поэтому они не скомпилируются.

Было предложено обернуть лямбду вstd::function, но у этого есть обратная сторона: вызов виртуальной функции при каждом вызове. Вызовы виртуальных функций не бесплатны, и вам придется платить эту стоимость за каждый элемент в вашем диапазоне. (виртуальные функции также препятствуют встраиванию)

Лучшее решение - использовать std::cref()так что итератор содержитstd::reference_wrapperчто по сути является указателем:

      static constexpr auto transformer = [](int i) { return -i; };
std::cout << *boost::min_element(v | boost::adaptors::transformed(std::cref(transformer))) << std::endl;

Посмотрите, как это работает на Coliru.

Это также работает для захвата лямбд, однако их нельзяstatic constexpr.

Другие вопросы по тегам