Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 std::set lambda comparison function

I want to create a std::set with a custom comparison function. I could define it as a class with operator(), but I wanted to enjoy the ability to define a lambda where it is used, so I decided to define the lambda function in the initialization list of the constructor of the class which has the std::set as a member. But I can't get the type of the lambda. Before I proceed, here's an example:

class Foo { private:      std::set<int, /*???*/> numbers; public:      Foo () : numbers ([](int x, int y)                        {                            return x < y;                        })      {      } }; 

I found two solutions after searching: one, using std::function. Just have the set comparison function type be std::function<bool (int, int)> and pass the lambda exactly like I did. The second solution is to write a make_set function, like std::make_pair.

SOLUTION 1:

class Foo { private:      std::set<int, std::function<bool (int, int)> numbers; public:      Foo () : numbers ([](int x, int y)                        {                            return x < y;                        })      {      } }; 

SOLUTION 2:

template <class Key, class Compare> std::set<Key, Compare> make_set (Compare compare) {      return std::set<Key, Compare> (compare); } 

The question is, do I have a good reason to prefer one solution over the other? I prefer the first one because it makes use of standard features (make_set is not a standard function), but I wonder: does using std::function make the code (potentially) slower? I mean, does it lower the chance the compiler inlines the comparison function, or it should be smart enough to behave exactly the same like it would it was a lambda function type and not std::function (I know, in this case it can't be a lambda type, but you know, I'm asking in general) ?

(I use GCC, but I'd like to know what popular compilers do in general)

SUMMARY, AFTER I GOT LOTS OF GREAT ANSWERS:

If speed is critical, the best solution is to use an class with operator() aka functor. It's easiest for the compiler to optimize and avoid any indirections.

For easy maintenance and a better general-purpose solution, using C++11 features, use std::function. It's still fast (just a little bit slower than the functor, but it may be negligible) and you can use any function - std::function, lambda, any callable object.

There's also an option to use a function pointer, but if there's no speed issue I think std::function is better (if you use C++11).

There's an option to define the lambda function somewhere else, but then you gain nothing from the comparison function being a lambda expression, since you could as well make it a class with operator() and the location of definition wouldn't be the set construction anyway.

There are more ideas, such as using delegation. If you want a more thorough explanation of all solutions, read the answers :)

like image 756
cfa45ca55111016ee9269f0a52e771 Avatar asked Feb 15 '13 13:02

cfa45ca55111016ee9269f0a52e771


People also ask

What is the correct syntax for lambda expression in C++11?

Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };

How do you pass a lambda function in C++?

Permalink. All the alternatives to passing a lambda by value actually capture a lambda's address, be it by const l-value reference, by non-const l-value reference, by universal reference, or by pointer.

Can std :: function store lambda?

Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

Is a lambda a std :: function?

Lambda's type One important thing to note is that a lambda is not a std::function .


1 Answers

It's unlikely that the compiler will be able to inline a std::function call, whereas any compiler that supports lambdas would almost certainly inline the functor version, including if that functor is a lambda not hidden by a std::function.

You could use decltype to get the lambda's comparator type:

#include <set> #include <iostream> #include <iterator> #include <algorithm>  int main() {    auto comp = [](int x, int y){ return x < y; };    auto set  = std::set<int,decltype(comp)>( comp );     set.insert(1);    set.insert(10);    set.insert(1); // Dupe!    set.insert(2);     std::copy( set.begin(), set.end(), std::ostream_iterator<int>(std::cout, "\n") ); } 

Which prints:

1 2 10 

See it run live on Coliru.

like image 106
metal Avatar answered Oct 14 '22 09:10

metal