A member function pointer must be invoked using the .* (or ->*) syntax, so it can't be passed to a higher-order function:
#include <vector>
void for_each(auto const& v, auto f) {
for (auto const& e : v)
f(e); // error: must use '.*' or '->*' to call pointer-to-member function in 'f (...)', e.g. '(... ->* f) (...)'
}
struct Foo {
void bar();
};
int main() {
std::vector<Foo> v(10);
for_each(v, &Foo::bar); // from here
}
The C++ Standard Library has two separate solutions to this: either I can use std::mem_fn() to get a free-function-like callable from a member function:
int main() {
std::vector<Foo> v(10);
for_each(v, std::mem_fn(&Foo::bar)); // OK
}
Or I can augment the higher-order function to use std::invoke (std::for_each already does this) instead of invoking the callable directly:
void for_each(auto const& v, auto f) {
for (auto const& e : v)
std::invoke(f, e); // OK
}
But, since the syntax (&Foo::bar)(Foo{}) is invalid at the current time, couldn't the standard make it valid and equivalent to calling std::mem_fn() first on the &Foo::bar?
Effectively, this would mean "absorbing" the std::mem_fn() utility in the language.
Would that be possible? Or, would it have undesired side effects? I can't see how it could break anything, considering that it's currently invalid syntax.
As I wrote the question, a possible answer came to my mind: SFINAE could be relying on that syntax being invalid.
It the following snippet, for instance, the second static_assert would fail if the standard started to allow calling (&Foo::bar)(Foo{}):
#include <type_traits>
#include <vector>
struct Foo {
void bar();
};
template<typename F, typename = void>
struct Trait : public std::false_type {};
template<typename F>
struct Trait<F, std::void_t<decltype(std::declval<F>()(std::declval<Foo>()))>>
: public std::true_type {};
auto constexpr freeBar = [](Foo){};
int main() {
static_assert(Trait<decltype(freeBar)>::value);
static_assert(!Trait<decltype(&Foo::bar)>::value);
}
However, in the comments to my delete self-answer it was pointed out that this cannot be a reason to prevent the standard from adopting the syntax I'm thinking about.
After all, and more in general, if we wanted not to break code which uses SFINAE to detect invalid code, we could practically not add anything to the standard.
The idea of treating a non-static member function as an ordinary function with the additional object parameter (as done, say, by Python's
C.fsyntax) has of course been proposed multiple times and has been the subject of questions here. One complication is that a pointer-to-member can refer to a virtual function and calls the most-derived function; allowing a pointer-to-member to be called would presumably perform dynamic dispatch on the first argument, which would be novel (albeit without "contradicting" anything).C++23 actually provides this for explicit-object member functions that cannot be virtual by providing ordinary pointers to functions for them:
Meanwhile, it's important to note that the syntax
(&Foo::bar)(Foo{})is not prima facie invalid: C++20 says that overload resolution proceeds for it as if it weref.bar(Foo{})for somefof typeFoo([over.match.call.general]/2). Of course, this call cannot actually be made, so performing overload resolution as if it could (potentially triggering ambiguities with static member functions with one more parameter) is not very helpful. Moreover, neither GCC nor Clang implements that overload resolution properly (instead claiming that [over.over] applies and fails), so C++23 changes overload resolution for that syntax to instead be compatible with the potential(!) feature of calling a pointer-to-member function.So the (perhaps unsatisfying) answer is that there are real challenges in making such a change work, but there has also been some progress in addressing them.