Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Passing function objects as lvalues and/or rvalues

I have a class that should filter its contents according to a user-supplied predicate. The interface I am given prescribes taking a reference to the predicate:

class Test {
  vector<int> data;
public:
  template <class PREDTYPE>
  void filter(PREDTYPE& pred) {
    return;
  }
};

I am also given a piece of test code, which looks roughly like this:

class Filter {
public:
  bool operator()(int) const {
    return false;
  }
};

int main() {
  Test test;
  test.filter(Filter());
}

This doesn’t compile, saying cannot bind non-const lvalue reference of type 'Filter&' to an rvalue of type 'Filter'. If I change the testing code to

int main() {
  Test test;
  Filter filter;
  test.filter(filter);
}

it works, but this is up to the end user, and I have no control over their behaviour. I tried overloading the filter method, creating a version which would accept the predicate by value and then pass it on by reference, but this doesn’t compile either, with the message call of overloaded 'filter(Filter&)' is ambiguous.

Hence my question: is it possible to construct a filter that would accept both rvalues and lvalues of a predicate?

like image 679
macleginn Avatar asked Mar 06 '23 21:03

macleginn


2 Answers

In short, yes it is (C++11)

You just have to rely on reference collapsing rules to make sure of that:

template <typename PREDTYPE>
void filter(PREDTYPE&& pred) { // notice the &&
    // ... Whatever
    // Use perfect forwarding *IF* you can
    std::forward<PREDTYPE>(pred)(some_stuff);
    // Do not use pred after it has been forwarded!
}

This will accept both rvalues and lvalues without having to rely on a const- reference (so your predicates can still be mutable). If you are stuck with older C++ standards, your best shot would be to use a const reference instead (with the caveat highlighted above) or embed your filter in an std::function and rely on implicit conversions at the call site.

like image 81
Rerito Avatar answered Mar 15 '23 14:03

Rerito


It depends on what the function does with the predicate. If it keeps a reference/pointer to it after it returns, you have to provide something which will live long enough. However, if it's not keeping any memory of the predicate object passed in, you can cast your predicate to an lvalue:

test.filter(static_cast<Filter&>(Filter()));

Or wrap that cast in a short utility function:

template <class T>
T& stay(T&& a)
{ return a; }

test.filter(stay(Filter()));
like image 30
Angew is no longer proud of SO Avatar answered Mar 15 '23 16:03

Angew is no longer proud of SO