Declaring Functions that Take Lambdas

Hi,

I have a linked list of Nodes that each contain a Human, looking something like this:

1
2
3
4
5
struct Node
  {
    Human* human;
    Node* next;
  };


I want to find a human in the linked list by one of their attributes, so I declared a function called, getHumanBy which will take a lambda expression. It's prototype looks like this:

Node* getHumanBy(std::function<bool(Human&)>& predicate);

It is defined like this:

1
2
3
4
5
6
7
8
9
World::Node* World::getHumanBy(std::function<bool(Human&)>& predicate)
{ 
  Node* iterator = root;
  while(iterator->next != nullptr)
  {
    if(predicate(iterator->human))
      return iterator;
  }
}


and is invoked like this:

1
2
3
4
5
6
 void World::reproduce()
{
  Node* myHuman = getHumanBy([](Human& currentHuman){ return currentHuman.getGender() == Human::Gender::MALE; });
  std::cout << myHuman->getName();
  std::cout << myHuman->getEyeColor();
}


The error I receive is:

1
2
3
World.cpp: In member function ‘void World::reproduce()’:
World.cpp:71:112: error: no matching function for call to ‘World::getHumanBy(World::reproduce()::<lambda(Human&)>)’
   Node* myHuman = getHumanBy([](Human& currentHuman){ return currentHuman.getGender() == Human::Gender::MALE; });


If it's not clear, I'd like to use `getHumanBy` much like `std::find_if` in vector.

Last edited on
Since you're using a singly linked list I'm using std::forward_list<> in the program below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# include <iostream>
# include <string>
# include <forward_list>

struct Human
{
    std::string m_name;
    Human (const std::string& name)
    : m_name (name){}
};

int main()
{
    std::forward_list<Human> myHumans{};

    myHumans.emplace_front("John");
    myHumans.emplace_front("Jane");
    myHumans.emplace_front("Jacob");

    auto findByName = [](const std::string& name, const Human& h){return h.m_name == name;};

    bool match{false};
    for (auto itr = myHumans.cbegin(); itr != myHumans.cend(); ++itr)
    {
        if(findByName("John", *itr))
        {
            std::cout << "found John \n";
            match = true;
            break;
        }
    }
    if(!match)std::cout << "no match \n";
}
I wonder if using a template parameter like the stl algorithms would give more flexibility than a lambda. Would like to hear the opinions of the experts.
Last edited on
The problem is that you pass predicate as a reference:

World::Node* World::getHumanBy(std::function<bool(Human&)>& predicate)

You cannot pass a temporary function object like you try to do. So either you change the paramter to const reference or by value.
Thomas1965 wrote:
I wonder if using a template parameter like the STL algorithms would give more flexibility than a lambda.

It's impossible to write the type of a lambda. Do you mean std::function<>?

I guess you're talking about something like
1
2
3
template <typename Predicate> 
World::Node* World::getHumanBy(Predicate&& predicate)
{ /*...*/ bool result = std::forward<Predicate>(predicate)(human); /* ... */ }

vs the OP's
World::Node* World::getHumanBy(std::function<bool(Human&)> const & predicate)

std::function's purpose is to homogenize groups of callable objects, not to be used individually where the genericity isn't required; it's not free. For this reason it is usually preferrable to let duck typing do the work for you and use a template parameter.

For any given template parameter, the exact requirements on it depend on the function implementation which is ultimately selected, but if we assume that it involves just a direct call like above (i.e., std::forward<Predicate>(predicate)(human)), then the template parameter Predicate must only satisfy FunctionObject.

Remember, however, that std::function can bind to anything which satisfies Callable with a particular signature. This is in fact more generic than the function template above.

Everything that satisfies FunctionObject also satisfies Callable, but the converse is not true. One example is member function pointers: they are Callable, but their call syntax is idiosyncratic and giving one to the function template above would result in a substitution failure (because member-function pointers don't satisfy FunctionObject). A contrived example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# include <functional>
# include <iostream>
struct A { 
  void mfn() const { std::cout << "here! (" << this << ")\n"; };
  void takes_callable(std::function<void(A*)> const& f) { f(this); }
  template <typename F>
  void takes_forwarding_ref(F&& f) { std::forward<F>(f)(this); } 
};


int main() {
  A a;
  auto mfp = &A::mfn;
  a.takes_callable(mfp);
  // error from call req'd here: not a function
  a.takes_forwarding_ref(mfp); 
}


But it is possible to use a lambda, a bind expression, or something equivalent to do the right thing:
a.takes_forwarding_ref([=](auto* p) { return (p->*mfp)(); });
which implies that the difference in genericity is minor. Further, overload resolution means that if it was really necessary, you could specially handle member-function pointers, although at that point std::function may begin to look more attractive.
Last edited on
@mbozzi,
sorry your code is far above my head.

I was thinking about sth. simple like this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename Predicate> 
World::Node* World::getHumanBy(Predicate predicate)
{ 
  Node* iterator = root;
  while(iterator->next != nullptr)
  {
    if(predicate(iterator->human))
      return iterator;
  }
return nullptr;
}

	

}
Last edited on
Hi Thomas
I've tried to re-jig my earlier post in line with your suggestion:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# include <iostream>
# include <string>
# include <forward_list>

struct Human
{
    std::string m_name;
    Human (const std::string& name)
    : m_name (name){}
};

template <typename Predicate>
auto getHumanBy(Predicate predicate) -> decltype(predicate)
{
    return predicate;
}

int main()
{
    std::forward_list<Human> myHumans{};

    myHumans.emplace_front("John");
    myHumans.emplace_front("Jane");
    myHumans.emplace_front("Jacob");

    auto findByName = [](const std::string& name, const Human& h){return h.m_name == name;};

    bool match{false};
    for (auto itr = myHumans.cbegin(); itr != myHumans.cend(); ++itr)
    {
        if(getHumanBy(findByName("John", *itr)))
        {
            std::cout << "found John \n";
            match = true;
            break;
        }
    }
    if(!match)std::cout << "no match \n";
}
@Thomas1965
Sorry, that wasn't very clear.

I was thinking about sth. simple like this

In your example the only thing you do with predicate involves calling it. (and copy - it's passed by value - but ignoring that)....

Because you call predicate, it must be a FunctionObject - that is, you must be able to use it on the left side of some parentheses like predicate(iterator->human) // line 7 or more generally like f(args).
http://en.cppreference.com/w/cpp/concept/FunctionObject

There are also some things that aren't FunctionObjects, but merely Callable. As well as all FunctionObjects, the Callable concept also contains some oddball stuff like reference wrappers and pointer-to-member functions and data. These things can be "called", but not in the usual way, or with the usual syntax: calling them like f(args) would be an error. Instead, the standard defines a function called std::invoke which describes what it means to call (invoke) every Callable object.
http://en.cppreference.com/w/cpp/concept/Callable

The point is that std::function accepts any Callable, but the STL-like function template will merely accept any FunctionObject. Since Callable is a superset of FunctionObject, std::function is marginally more general.
Last edited on
Topic archived. No new replies allowed.