Many algorithms from the standard library accept a unary predicate with a signature of bool (Type & item)
so supplying a pointer to a non-static member function directly is not working. This seems to be rather restrictive considering that it seems to be possible to lift such a restriction by replacing direct invocation of operator ()
on the predicate with a call to std::invoke
. Maybe the proposed approach has some drawbacks that I've overlooked?
Note: the non-static member function referred to in this question is supposed to differ from a regular function predicate only in that an item reference is passed as an implicit parameter rather than an explicit parameter.
Example code (online compiler):
#include <array>
#include <algorithm>
#include <iostream>
#include <functional>
#include <cassert>
template<typename TForwardIterator, typename TPredicate> TForwardIterator
my_find_if
(
const TForwardIterator p_items_begin
, const TForwardIterator p_items_end
, TPredicate && predicate
)
{
TForwardIterator p_item(p_items_begin);
// while((p_items_end != p_item) && (!predicate(*p_item)))
while((p_items_end != p_item) && (!::std::invoke(predicate, *p_item)))
{
++p_item;
}
return(p_item);
}
class t_Ticket
{
private: int m_number;
public:
t_Ticket(const int number): m_number(number) {}
public: bool
Is_Lucky(void) const {return(8 == m_number);}
};
int main()
{
::std::array<t_Ticket, 10> tickets{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// using standard library
auto p_ticket1(::std::find_if(tickets.begin(), tickets.end(), [](const t_Ticket & ticket) {return(ticket.Is_Lucky());}));
// still works
auto p_ticket2(my_find_if(tickets.begin(), tickets.end(), [](const t_Ticket & ticket) {return(ticket.Is_Lucky());}));
// same thing, but shorter and not sufferring from potential lambda code duplication
auto p_ticket3(my_find_if(tickets.begin(), tickets.end(), &t_Ticket::Is_Lucky));
// using standard library like this won't compile
//auto p_ticket4(::std::find_if(tickets.begin(), tickets.end(), &t_Ticket::Is_Lucky));
assert(p_ticket1 == p_ticket2);
assert(p_ticket2 == p_ticket3);
return(0);
}
Firstly, assuming that the question is:
Why can't algorithms take a generic
Callable
as their predicate/action?
I can think of multiple reasons:
The Callable
concept was introduced in C++11 - pre-C++11 algorithms were not designed with it in mind.
Accepting any Callable
would require expanding concepts such as Predicate
and allowing algorithms to conditionally take an additional argument for pointers to member functions. This might lead to unnecessary complexity that can be avoided by simply restricting the interface to FunctionObject
and forcing the user to do the "binding".
This would increase the amount of overloads significantly, considering that there also are execution policy overloads now. Alternatively, it could make every algorithm a variadic template where the parameter pack can contain either zero or one arguments (in case *this
needs to be provided for a Callable
).
std::invoke
is not constexpr
. Using it might prevent algorithms to be marked as constexpr
in the future.
If you are referring to the particular case where:
The algorithm operates over a homogeneous range of T
.
The predicate is executed on every element of type T
of the range.
T
has a member function that could be used as a predicate.
Then, I can still think of some possible reasons:
std::invoke
is still not constexpr
. We could avoid using std::invoke
for .*
, but then we would need to provide two separate implementations per algorithm.
If the member function is a template
or an overload set, then this would fail to compile and confuse beginners. Generic lambdas do not have this issue.
It would complicate the requirements for algorithms - there would need to be some sort of restriction on the type/signature of the member function that makes sure it is usable on the range of T
.
Or maybe... it just hasn't been proposed yet. If you still think it's a valuable thing to have after the reasons I came up with, you can start here in order to learn how to write a proposal: https://isocpp.org/std/submit-a-proposal
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