1

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.

17
  • 4
    whats wrong with the first version? Seems to be fine, no? Don't try to minic Java syntax in C++, its not going to end well Commented Nov 14, 2023 at 19:12
  • Trying to make C++ look like Java (or any other language) will result in 1) Buggy programs. 2) Inefficient programs, and 3) Programs that look weird to a C++ programmer. You violated 3), and possbily 1) or 2). Commented Nov 14, 2023 at 19:15
  • 2
    Lambdas are the preferred way. Sometimes it's possible to take an address of some function in the std namespace, but the standard forbids it, except for a very limited set of designated addresable functions. Commented Nov 14, 2023 at 19:18
  • 1
    @PepijnKramer Yeah, could even make it a template and then you're basically rolling out your own std::empty() clone. Commented Nov 14, 2023 at 19:28
  • 1
    @Remy Certainly a possibility tool, I just wanted to point out it doesn't have to be a lambda ;) Commented Nov 14, 2023 at 19:28

3 Answers 3

2

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);
}
Sign up to request clarification or add additional context in comments.

Comments

0

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.

1 Comment

One could have used std::bind_front(&std::vector<int>::empty) but the standard says it's unspecified behaviour.
0

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.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.