3

I'm trying to write a higher-order function via Lambda in C++, and got this code.

void ProcessList::SortCol(std::string col, bool flag) {

    auto CmpGenerator = [&]<typename T>
        (std::function<T(const Process &itm)> func) {
        return (flag? [&](const Process &a, const Process &b) {
                            return func(a) < func(b);}
                    : [&](const Process &a, const Process &b) {
                            return func(a) > func(b);}
            );
        };

    std::function<bool(const Process &a, const Process &b)> cmp;

    if (col == "PID") {
        cmp = CmpGenerator([](const Process &itm) {
            return itm.GetPid();
        });
    }
    else if (col == "CPU") {
        cmp = CmpGenerator([](const Process &itm) {
            return itm.GetRatioCPU();
        });
    }
    else if (col == "COMMAND") {
        cmp = CmpGenerator([](const Process &itm) {
            return itm.GetCmd();
        });
    }
    std::sort(lst.begin(), lst.end(), cmp);
}

However when compiling, g++ reported that no match for call to

no match for call to ‘(ProcessList::SortCol(std::string, bool)::<lambda(std::function<T(const Process&)>)>) (ProcessList::SortCol(std::string, bool)::<lambda(const Process&)>)’

What's wrong here with the code?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982

1 Answers1

3

The primary problem in this example is that a lambda is not a std::function. See this question.

CmpGenerator deduces its argument as std::function<T(Process const&)>, but a lambda will never match that, so deduction fails.

Furthermore, the body of CmpGenerator tries to return one of two different lambdas - which have different types. Those lambdas are not convertible to each other, so the conditional expression will fail. But we also can't deduce the return type of CmpGenerator since the two different lambdas have different types.


We can start by doing this completely by hand. std::ranges::sort takes a projection, which is very helpful in this regard:

if (col == "PID") {
    if (increasing) { // <== 'flag' is not a great name
        std::ranges::sort(lst, std::less(), &Process::GetPid);
    } else {
        std::ranges::sort(lst, std::greater(), &Process::GetPid);
    }
} else if (col == "CPU") {
    // ...
}

This gives the structure that we need to abstract: we're not generating a comparison object, we're generating a call to sort.

That is:

auto sort_by = [&](auto projection){ // <== NB: auto, not std::function
    if (increasing) {
        std::ranges::sort(lst, std::less(), projection);
    } else {
        std::ranges::sort(lst, std::greater(), projection);
    }
};

if (col == "PID") {
    sort_by(&Process::GetPid);
} else if (col == "CPU") {
    sort_by(&Process::GetRatioCPU);
} else if (col == "COMMAND") {
    sort_by(&Process::GetCmd);
}
Barry
  • 286,269
  • 29
  • 621
  • 977