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()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With