This question (Why does INVOKE facility in the C++11 standard refer to data members?) asks why INVOKE discusses data members but ignores how they are actually invoked.
This question (What is std::invoke in c++?) discusses why they are accessed, but why they are not called if callable.
Define INVOKE(f, t1, t2, …, tN) as follows:
- (1.1) (t1.*f)(t2, …, tN) when f is a pointer to a member function of a class T and is_base_of_v<T, remove_reference_t<decltype(t1)>> is true;
- (1.2) (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of a class T and remove_cvref_t<decltype(t1)> is a specialization of reference_wrapper;
- (1.3) ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a class T and t1 does not satisfy the previous two items;
- (1.4) t1.*f when N == 1 and f is a pointer to data member of a class T and is_base_of_v<T, remove_reference_t<decltype(t1)>> is true;
- (1.5) t1.get().*f when N == 1 and f is a pointer to data member of a class T and remove_cvref_t<decltype(t1)> is a specialization of reference_wrapper;
- (1.6) (*t1).*f when N == 1 and f is a pointer to data member of a class T and t1 does not satisfy the previous two items;
- (1.7) f(t1, t2, …, tN) in all other cases.
1.4 through 1.6 deal with access to pointers to data members, which makes sense given functors and stored callables. What I don't understand is why it doesn't call those members, but instead simply dereferences them? I would expect that 1.4 would parallel the syntax of 1.1 and er...invoke the object in question if f
possesses an operator ()
.
Why is this restriction in place, and what purpose does it serve?
Here's some code for clarification:
#include <functional>
#include <iostream>
struct func1
{
void operator()() { std::cout << "Invoked functor\n"; }
};
void func2()
{
std::cout << "Invoked free function\n";
}
struct D1 {};
struct T1 {
func1 f1;
void func3() { std::cout << "Invoked member function\n"; }
D1 d1;
};
int main()
{
T1 t1;
func1 free_f1;
std::invoke(&T1::f1, t1); //does nothing
std::invoke(&func1::operator(), t1.f1); //okay, so there is a workaround, if clumsy
std::invoke(&func2); //calls func2
std::invoke(&T1::func3, t1); //calls func3
std::invoke(&T1::d1, t1); //does nothing (expected)
std::invoke(free_f1); //works on non-member functors
return 0;
}
This compiles nicely, but only calls func1()
on the second call to invoke
. I understand why INVOKE does nothing but dereference the first argument when it is not a callable object. My question is why does the standard not allow for calling callable pointers to data members, i.e. why does the Standard not mandate that f1
is called in the first usage of std::invoke
above?
Edit: since std::invoke
was added in C++17, I'm tagging this question as such hoping someone involved in the process can shed some light.
Here's the original paper for adding std::invoke()
, which actually explains in its motivation that it wants to handle functors uniformly:
Although the behaviour of the INVOKE expression may be reproduced by combination of the existing standard library components, separate treatment of the functors and member pointers is required in such solutions.
In the code above, you can see this works...just not for pointers to member data that are functors themselves. Is this simply an oversight?