Is it possible to put lambda expressions into a map or list in C++?

Viewed 8126

@DanielLangr @luxun @cdhowie sorry for the XY problem. i am not sure i can explain well, but i try my best. the situation is almost like this: there is a base object "Worker" and some children. chef、tailor... children has the same action like walk、run、sleep...but different skill,chef can make food, tailor can Make clothes. Invoker call Worker dothings but do not exactly know their profession.so i add a interface dothings(Thing) on Worker the base object. Thing is an enum,value is MakeFood、MakeClothes...

Worker *w = new Chef();
w->dothings(MakeFood);//
w->dothings(MakeClothes);//throw exception "w do not have skill"

so i think meybe use a container in children that describe what it can do and how to do.

hope i explained clearly.and is there a better solution?

I want to put different lambda expressions into a list or Qmap, like below.

Qmap<String, lambda> map;
map.insert("first",[](int i) -> int {return i;});
map.insert("second",[](string s) -> string {return s;});

Is it possible in C++? And what is the type of lambda?

3 Answers

It is possible but using function wrapper.

For example,

std::map<std::string, std::function<void(std::string)>> my_map;
my_map.emplace("first", [](std::string i) { std::cout << i << std::endl; });

However, if you want to pass any type of argument to your function and return any type from your lambda/function, use boost::any. You also use std::any if you are using C++17 or above.

EDIT:

A working example:

#include <iostream>
#include <string>
#include <functional>
#include <map>
#include <boost/any.hpp>

int main()
{
    auto any = [](boost::any i)
    {
        std::cout << "In any" << std::endl;
        if (i.type() == typeid(int))
            std::cout << boost::any_cast<int>(i) << std::endl;
        return boost::any(1000);
    };
    std::map<std::string, std::function<boost::any(boost::any)>> my_map;
    my_map.emplace("first", any);
    my_map.emplace("second", [](boost::any i) -> boost::any { });
    auto ret = my_map["first"](100);
    std::cout << boost::any_cast<int>(ret) << std::endl;
    return 0;
}

Outputs:

In any
100
1000

With any, the solution may look like as follows:

auto lambda1 = [](int i) { return i; };
auto lambda2 = [](std::string s) { return s; };

std::map<std::string, std::any> map;
map["first"] = lambda1;
map["second"] = lambda2;

std::cout << std::any_cast<decltype(lambda1)>(map["first"])(-1) << std::endl;
std::cout << std::any_cast<decltype(lambda2)>(map["second"])("hello") << std::endl;

I am not familiar with Qmap and String, so I used the types from the C++ Standard Library.

Live demo: https://godbolt.org/z/8XK8de


Alternatively, you can also additionally use std::function if you want to avoid those decltypes:

std::map<std::string, std::any> map;
map["first"] = std::function<int(int)>( [](int i) { return i; } );
map["second"] = std::function<std::string(std::string)>( [](std::string s) { return s; } );

std::cout << std::any_cast<std::function<int(int)>>(map["first"])(-1) << std::endl;
std::cout << std::any_cast<std::function<std::string(std::string)>>(map["second"])("hello") << std::endl

Live demo: https://godbolt.org/z/XAc3Q2


However, as other pointed out to, this really seems to be an XY problem.

It is possible as long as you are trying to insert the same lambda type ( your example has different lambda types) You have to be careful how you do it but it does work. For example

#include <iostream>
#include <map>


int main(){
    auto factory = [](int i){
        return [=](int j){return i+j;};
    };

    using L = decltype(factory(0));

    std::map<int,L> map;

    map.emplace(0,factory(0));
    map.emplace(7,factory(7));
    std::cout << map.at(0)(3) << std::endl ;
    std::cout << map.at(7)(3) << std::endl ;

}

outputs

3
10

as expected and not a std::function in sight! However the following does not work

#include <iostream>
#include <map>


int main(){
    auto factory = [](int i){
        return [=](int j){return i+j;};
    };

    using L = decltype(factory(0));

    std::map<int,L> map;

    map[0]=factory(0);
    map[7]=factory(7);
    std::cout << map[0](3) << std::endl ;
    std::cout << map[7](3) << std::endl ;

}

Using the indexing operator tries to use copy assignment whereas emplace doesn't.

https://godbolt.org/z/co1vno6xb

Related