petersohn
petersohn

Reputation: 11720

Using Boost adaptors with C++11 lambdas

I tried to compile this code:

#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;
}

The compilation failed with the following error message (after a long template instantiation novel):

/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

I googled the problem, and found this in the Boost Users mailing list archive. It suggested that using #define BOOST_RESULT_OF_USE_DECLTYPE would solve the problem. I put it into the very beginning of my code, but it still doesn't compile. The length of the error message seems to be much shorter, but the error message at the end is the same. I'm currently using Boost 1.50.

What can be the problem here? Is there any way to make this work?

Upvotes: 21

Views: 6473

Answers (5)

Brandlingo
Brandlingo

Reputation: 3142

As others have already mentioned, the problem is, that std::min_element() (which is simply wrapped by boost::min_element()) wants to copy the begin iterator of the range. But this iterator contains an instance of the transforming callable (same thing happens with a filtering callable) which therefore must be copied as well. But lambdas are not copyable so this will not compile.

It was suggested to wrap the lambda in a std::function, but this has the downside of a virtual function call with every invocation. Virtual function calls are not for free and you'll have to pay that cost for every element in your range. (virtual functions also hinder inlining)

The better solution is to use std::cref() so that the iterator contains a std::reference_wrapper which is basically a pointer:

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

See it working on Coliru.

This also works for capturing lambdas, however those cannot be static constexpr.

Upvotes: 0

otter
otter

Reputation: 525

With C++17 feature class template argument deduction you can wrap with std::function simplier, like this:

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

Upvotes: 0

Michael F Hancock
Michael F Hancock

Reputation: 121

You can turn a non-capturing lambda into a function pointer by putting a "+" in front of it.

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;

Upvotes: 11

ecatmur
ecatmur

Reputation: 157324

This is covered at http://boost.2283326.n4.nabble.com/range-cannot-use-lambda-predicate-in-adaptor-with-certain-algorithms-td3560157.html and https://svn.boost.org/trac/boost/ticket/4189 - the problem is that some algorithms expect to be able to copy-construct (and default-construct, and copy-assign) their predicate, which can't be done with a lambda.

The workaround is to wrap the lambda in a std::function:

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

I've asked (at Inferring the call signature of a lambda or arbitrary callable for "make_function") for a way to write a make_function such that one can just write:

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

Upvotes: 6

ForEveR
ForEveR

Reputation: 55887

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

But you can use this, that works well.

#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 works fine in VS2012 for example.

Upvotes: 10

Related Questions