1
\$\begingroup\$

I have some predicate objects that take some value and return the result of some criteria. (My real implementation is more complex, this is only a minimal example):

Run on Compiler Explorer

struct PredEven
{
    bool passes(int x) const { return x%2 == 0; }
};
struct PredGreaterThan
{
    int limit;
    bool passes(int x) const { return x > limit; }
};
using Predicate = std::variant<PredEven, PredGreaterThan>;

I store objects of these types in a std::array and now want to test some value against all of these. The straight-forward implementation would be this:

const std::array<Predicate, 2> predicates = {PredEven{}, PredGreaterThan{3}};
const int valToTest = 4;

bool allPassed1 = true;
for(const auto& var : predicates)
{
    allPassed1 &= std::visit([&valToTest](const auto& p){ return p.passes(valToTest); }, var);
}

However, I really should be using std::all_of for this. Better yet, the ranges version:

const bool allPassed2 = std::ranges::all_of(predicates, [&valToTest](const auto& var)
    { return std::visit([&valToTest](const auto& p){ return p.passes(valToTest); }, var); });

This code is concise but rather hard to read (and to format). Particularly the nested lambda isn't great. It does have the advantage that the result can be const. Is there any way to get rid of the nested lambda or make this more readable?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The easiest thing to do is to hide away the complexity in a function or class. One possibility is to create a function that returns the nested lambda, like so:

static auto passes(auto valToTest) {
    return [valToTest](const auto& var) {
        return std::visit([&valToTest](const auto& p) {
            return p.passes(valToTest);
        }, var);
    };
}

const bool allPassed3 = std::ranges::all_of(predicates, passes(valToTest));

There are other ways to reduce the amount of lambdas perhaps, like using std::bind and/or creating a class that has an operator(), but in the end that will probably be equally or even more hard to read as lambdas are basically shorthand for that.

\$\endgroup\$
1
  • \$\begingroup\$ That's what I feared. The idea of giving the lambdas a concise name is good, I settled on a local variable instead of a function. \$\endgroup\$
    – perivesta
    May 11, 2022 at 10:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.