C++ equivalent of Java's method references?

192 Views Asked by At

I have a std::vector<std::vector<int>>, from which I want to check whether there is at least 1 sub-vector that's empty. So I have this:

std::any_of(vec.begin(), vec.end(), [](const auto& subvec)  {
     return subvec.empty();
});

Is there something like this in C++14?

std::any_of(vec.begin(), vec.end(), std::vector<int>::empty);

I tried the above syntax, which didn't work. Nor did sticking a & in front of the method work, as one source claimed.

3

There are 3 best solutions below

1
On BEST ANSWER

Using the lambda is the readable way.

Only for illustration lets explore other ways. The predicate p needs to be (from cppreference):

The expression p(v) must be convertible to bool for every argument v of type (possibly const) VT, where VT is the value type of InputIt, regardless of value category, and must not modify v. Thus, a parameter type of VT& is not allowed, nor is VT unless for VT a move is equivalent to a copy(since C++11). ​

Implicit conversions aside, this basically means, the predicate must be a callable with signature bool (const std::vector<int>&).

std::function is a candiate. It comes with considerable overhead, because its main purpose is type erasure. We do not need type erasure, but std::function also has a mechanism to turn a member function pointer into a callable where the object is passed to the function. Ie we can transform &std::vector<int>::empty, a member function pointer, into something with signature bool (const std::vector<int>&). So far so good, but this transformation isnt implicit and does not play well with class template argument deduction. As a consequence the syntax is rather clumsy:

int main () {
    std::vector<std::vector<int>> vec;
    std::any_of(vec.begin(),vec.end(),std::function<bool(const std::vector<int>&)>(&std::vector<int>::empty));
}

Live Demo

Um... We can turn the member function pointer into a callable with right signature, but std::function isnt really what is needed here and it does not help for terse syntax. How about writing a custom wrapper:

template <typename T> struct class_type;
template <typename C> struct class_type<bool(C::*)() const noexcept> { using type = C;};

template <auto F>
struct bool_member_to_functor {    
    using type = typename class_type<std::decay_t<decltype(F)>>::type;
    bool operator()(const type& c) {
        return (c.*F)();
    }
};

int main (int argc, char** argv)
{
    std::vector<std::vector<int>> vec;
    std::any_of(vec.begin(),vec.end(),bool_member_to_functor<&std::vector<int>::empty>{});
}

This leads to nice syntax on the call. Passing the member function directly is not possible, but this is as close as it can get. However, class_type is a bit of a cheat here. You'd need more specializations to cover all const / non-const, not noexcept etc variants. And it gets much worse when the member function is overloaded. Its not something you actually want to write.

Live Demo

Conclusion: A lambda expression is the lightweight, easy to read way to wrap the member function. I doubt it can get any more readable.

0
On

In the comments you say:

Nothing glaringly wrong with it, other than that if there is indeed a shorter and (arguably) more readable way to write it,

So it seems what you really want is to use named functions (a good programming practice). We can do this and still use lambdas. Just create a named lambda.

#include <algorithm>

auto isEmpty = [](std::vector<int> const& v)  {return v.empty(); /* or std::empty(v) */};

int main()
{
    std::vector<std::vector<int>>   vec;

    auto f = std::any_of(std::begin(vec), std::end(vec), isEmpty);
}
0
On

The problem is flexible overloading rules in C++ and lack of semantics for overload set management. Member functions can be overloaded over CV as well as reference(rvalue/lvalue) qualifications of their object:

struct foo{
    void bar()const &;
    void bar()&&

    void bz();
};

Now the expression &foo::bar becomes ambiguous: there's one overload with a const lvalue object, as well as one with an rvalue mutable object. The only ways to disambiguate the expression are:

  1. explicit casting:
static_cast<void (foo::*) const &()>(&foo::bar)
  1. Or binding to object at call site, which can only be wrapped as a lambda.

If the function is not overloaded however, a function pointer can be created by taking its address: &foo::bz works without any problems.

For the sake of the above difficulties the <ranges> library - pioneered by Eric Neibler - introduced the Neibloid pattern. The idea can be implemented for any none-member functions. The article provided in the above link is not a direct answer to your question. But it is authored by a hyperactive member of C++ community and shows you lots of challenges and technicalities faced in this language.