问题
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?
回答1:
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.
回答2:
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.
回答3:
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()));
回答4:
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.
来源:https://stackoverflow.com/questions/51187685/c-passing-function-objects-as-lvalues-and-or-rvalues