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.