MBZ
MBZ

Reputation: 27592

override map::compare with lambda function directly

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'?

Upvotes: 25

Views: 20547

Answers (4)

NoSenseEtAl
NoSenseEtAl

Reputation: 30026

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;
    }
}

Upvotes: 4

Borgleader
Borgleader

Reputation: 15916

#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.

Upvotes: 8

Jake
Jake

Reputation: 757

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!

Upvotes: 5

zaufi
zaufi

Reputation: 7119

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

Upvotes: 26

Related Questions