25

Trying to override map::compare function using lambda, it seems that the following solution works.

auto cmp = [](const int&a, const int& b) { return a < b; };
std::map<int, int, decltype(cmp)> myMap(cmp);

But, I had to define cmp first and use it later.
Can I do this without defining 'cmp'?

5
  • 2
    cmp is not a function pointer. Commented Aug 4, 2013 at 16:50
  • 1
    Do what? ints already compare to each other. What are you trying to do? And why do you not want to use a function? Commented Aug 4, 2013 at 16:50
  • Yeah, function pointer was not an accurate term. Edited the question to make question more clear. Commented Aug 4, 2013 at 16:52
  • 1
    So what approach did you use that failed? We know you want something other than your example but not what your ultimate goal is. Commented Aug 4, 2013 at 17:02
  • But cannot captureless lambas be converted to a function pointer? Commented Aug 4, 2013 at 19:18

4 Answers 4

26

No, you can't use lambda in unevaluated context -- i.e. template parameters as in your example. So you must define it somewhere else (using auto) and then use decltype... the other way, as it was mentioned already is to use an "ordinal" functors

If your question is about "how to use lambda expression *once* when define a map" you can exploit implicit conversion of lambdas to std::function like this:

#include <iostream>
#include <functional>
#include <map>

int main()
{
    auto m = std::map<int, int, std::function<bool(const int&, const int&)>>{
        [](const int& a, const int& b)
        {
            return a < b;
        }
    };
    return 0;
}

you may introduce an alias for that map type to reduce typing later...

Sign up to request clarification or add additional context in comments.

1 Comment

However, using std::function would add a level of indirection and make it less efficient.
8
#include <iostream>
#include <functional>
#include <map>
#include <typeinfo>

typedef std::map< int, int, std::function<bool(const int&, const int&)> > MyMap;

int main()
{
    auto cmp = [](const int& a, const int& b) { return a < b; };
    MyMap map(cmp);

    return 0;
}

Using std::function to provide the appropriate type signature for the comparator type you can define your map type and then assign any lambda compare you wish to.

4 Comments

This is what the OP is asking for, but really, introducing std::function feels like a bad solution. How about an alias template instead? template<typename Comp> using MyMap = std::map<int, int, Comp>; and then you don't have to define the comparator until you instantiate the map.
@Praetorian I'm stuck with an inferior compiler (VS2012) which doesn't support template aliases so I have no experience with them.
template aliases wouldn't help anyway: you can't use lambda in unevaluated context, but you have to specify a Comp parameter anyway (at instantiation point).
but you can use std::function<> in a template alias. see my post for example. I actually think std::function ins't a bad solution if there is a compelling reason to handle multiple maps with many different comparators especially if some capture a variable (in which case an arbitrary number of unique comparisons might exist).
5

You could do something like this where the type of the map is deduced from the function you pass to a function.

#include <map>

template<class Key, class Value, class F>
std::map<Key, Value, F> make_map(const F& f) {
    return std::map<Key, Value, F>{f};
}

int main() {
    auto my_map = make_map<int, int>([](const int&a, const int& b) { return a < b; });
    my_map[10] = 20;
}

I don't see a ton of reason for doing this but I wont say it's useless. Generally you want a known comparator so that the map can be passed around easily. With the setup above you are reduced to using template functions all the time like the following

tempalte<class F>
void do_somthing(const std::map<int, int, F>& m) {

}

This isn't necessarily bad but my instincts tell me that having a type which can ONLY be dealt with by generic functions is bad. I think it works out fine for lambda functions but that's about it. The solution here is to use std::function

#include <map>
#include <functional>

template<class Key, class Value>
using my_map_t = std::map<Key, Value, std::function<bool(const Key&, const Key&)>>;

int main() {
    my_map_t<int,  int> my_map{[](const int&a, const int& b) { return a < b; }};
    my_map[10] = 20;
}

Now you can use any predicate you want and you have a concrete type to work with, my_map

hope this helps!

Comments

4

Since C++20:

If no captures are specified, the closure type has a defaulted default constructor.

Thus you can do this:

std::map<int, int, decltype([](const int&a, const int& b) { return a < b; })> myMap;


int main() {
    myMap.insert({7, 1});
    myMap.insert({46, 2});
    myMap.insert({56, 3});
    for (const auto& [key,value]:myMap) {
        std::cout <<  key <<  "  " << value << std::endl;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.