Bad function call when using a std::function as a comparator

174 Views Asked by At

I'm using lambdas instead of a comparator class for my set, but I'm running into issues.

I have something like this:

class Processor {
    
private:
    const function<bool(std::string, std::string)> _lenComp = [](string a, string b) { return a != b && a.size() >= b.size(); };
    set<string, decltype(_lenComp)> _inputSet;

// ...
    
public:
    Processor() : _inputSet() {
    }

    void addLine(string line) {
        _inputSet.insert(line);
    }
    

When I create a Processor instance and call addLine 2 times I get a bad function call exception. How do I properly initialize this class?

2

There are 2 best solutions below

0
Ahmed AEK On BEST ANSWER

decltype(_lenComp) is function<bool(std::string, std::string)> which is an empty std::function, so you get bad call when calling it.

what you wanted was the type of the lambda stored in it, you should define that with static constexpr auto, because you are not allowed to know the type of the lambda.

class Processor {
    
private:
    static constexpr auto _lenComp = [](string a, string b) { return a != b && a.size() >= b.size(); };
    set<string, decltype(_lenComp)> _inputSet;
};
0
Greg Inozemtsev On

To expand on the other answer: in this line the type of the lambda and the type of _lenComp are not the same:

const function<bool(std::string, std::string)> _lenComp = [](string a, string b) { return a != b && a.size() >= b.size(); };

std::function<bool(string, string)> is a generic wrapper functor that can hold any kind of function that returns a bool and takes two strings, be it a lambda, or a regular function, or a static method. You can reassign the actual implementation that this container wraps.

What you have done is created a lambda which has an opaque (hidden) type and stored it in an instance of the wrapper. Then you passed the type of the wrapper into the std::set template.

When _inputSet is constructed it has no knowledge of your lambda. It will construct a new instance of the comparator type it was given, which in your case is an std::function. And a default-constructed std::function is empty.

Another fix for the above if you wanted to keep std::function would be to pass the instance of the comparator that you already have to the set when it is constructed, like this:

// ...

    const function<bool(std::string, std::string)> _lenComp = [](string a, string b) { return a != b && a.size() >= b.size(); };
    set<string, decltype(_lenComp)> _inputSet;

public:
    Processor(): _inputSet({}, _lenComp) {}

// ...