ForeverLearning
ForeverLearning

Reputation: 6685

Trouble with Nested Lambdas

I am probably making some elementary mistake here but given:

std::array<int, 3> arr = { 1, 2, 3 };
std::vector<int> vecint;
vecint.push_back(1);
vecint.push_back(2);

This is one obvious way to compare the elements in arr with the ones in vecint.

std::for_each(vecint.begin(), vecint.end(), [&arr](int vecvalue) {
    for (auto arritr = arr.begin(); arritr != arr.end(); ++arritr) {
        if (vecvalue == *arritr) {
            std::cout << "found!!! " << vecvalue << "\n";
        }
    }
});

However, should I be able to do it like this too?

std::for_each(vecint.begin(), vecint.end(), [&arr](int vecvalue) {
    if (std::find(arr.begin(), arr.end(), [=](int arrval) { return vecvalue == arrval; }) != arr.end()) {
        std::cout << "found!!! " << vecvalue << "\n";
    }
});

The latter fails to compile in VC11 with the following error:

1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility(3186): error C2678: binary '==' : no operator found which takes a left-hand operand of type 'int' (or there is no acceptable conversion)

What am I missing?

Upvotes: 0

Views: 360

Answers (1)

dyp
dyp

Reputation: 39111

cppreference on std::find and std::find_if

std::find takes a value to compare with as the third parameter, whereas std::find_if takes a UnaryPredicate (a function object taking one parameter). You probably just had a typo / wanted to use std::find_if.

Using std::find_if works for me. Live example.

#include <array>
#include <vector>
#include <iostream>
#include <algorithm>


int main()
{
    std::array<int, 3> arr = {{ 1, 2, 3 }};
    std::vector<int> vecint;
    vecint.push_back(1);
    vecint.push_back(2);

    std::for_each
    (
        vecint.begin(), vecint.end(),
        [&arr](int vecvalue)
        {
            if (std::find_if(arr.begin(), arr.end(),
                             [=](int arrval) { return vecvalue == arrval; })
                != arr.end())
            {
                std::cout << "found!!! " << vecvalue << "\n";
            }
        }
    );
}

A simpler version is of course to use std::find (correctly):

    std::for_each
    (
        vecint.begin(), vecint.end(),
        [&arr](int vecvalue)
        {
            if (std::find(arr.begin(), arr.end(), vecvalue) != arr.end())
            {
                std::cout << "found!!! " << vecvalue << "\n";
            }
        }
    );

Then, there's of course the range-based-for-loop variant, if your compiler supports it:

for(auto const& ve : vecint)
{
    for(auto const& ae : arr)
    {
        if(ve == ae)
        {
            std::cout << "found!!! " << ve << "\n";
        }
    }
}

If your ranges are sorted, there are faster algorithms to get the intersection. Either you write your own loop to invoke an action for each element in the intersection, or you let the Standard Library copy the intersection into a new container:

#include <iterator> // additionally

std::vector<int> result;
std::set_intersection(arr.begin(), arr.end(), vecint.begin(), vecint.end(),
                      std::back_inserter(result));
for(auto const& e : result)
{
    std::cout << e << std::endl;
}

What happens under the hood - why you get that error:

std::find is defined as (from cppreference):

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );

That is, the type of value and the type of the iterators are independent. However, in the implementation of std::find, there has to be a comparison like:

if(*first == value) { return first; }

And at this point, you're comparing an int (type of the expression *first) with a lambda (type of value) more precisely: with a closure type. This is ill-formed (luckily), as there's no conversion from a lambda to an int, and there's no comparison operator declared that's applicable here.

Upvotes: 3

Related Questions