Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Got an Error when using C++20 Polymorphism Lambda Function

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?

like image 464
Jane_leaves Avatar asked Nov 29 '25 18:11

Jane_leaves


1 Answers

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);
}
like image 68
Barry Avatar answered Dec 02 '25 08:12

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!