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 :)
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; };
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.
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.
Lambda's type One important thing to note is that a lambda is not a std::function .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With