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 :)
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.