简体   繁体   中英

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?

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.

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()));

You might want to change your member function signature to

template <class PREDTYPE> void filter(PREDTYPE&& pred)

Here, the predicate is passed as a forwarding reference , which collapses to an lvalue reference for lvalues passed in an rvalue references to rvalues passed in.

Usually, function objects should be passed by value. So don't overload and change your signature to

template <class PREDTYPE> void filter(PREDTYPE pred)

An explanation why this is better can be found here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM