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.
for_each(v, std::bind(&Foo::bar, _1));
? – Foolhardystd::bind
is toxic, so that is a reason not to. (std::bind
makes that mean FAR MORE than you intend it to, involving the semantics of recursive calls to bind. Use of bind in generic code is a BAD IDEA) – Pantelegraphbind
and lambda optimizes pretty well (and usually to the same code). – Foolhardystd::ranges::for_each
since it useinvoke
– Leeuwardenstd::bind
. Those often include people who have tried to implement it. At least some of those that have implemented it advise against using it, because full awareness of those quirks requires a lot of effort. And lambdas solve the same problem without having to know about those quirks. And you need to know how lambdas work anyhow, so why learn two sets of quirks? Look into how bind placeholders and passing bind objects to bind work: in generic code things can go insanely wonky. Now specify what your code that uses bind acts like... heh. – Pantelegraphfor_each(v, &Foo::bar);
work out of the box – Brittenyfor_each
old implementation, or another algorithm altogether which doesn't internally usestd::invoke
, right? – Storzstd::bind
is so bad? – Storzstd::bind
as an argument to astd::bind
expression. Now explain in a simple short sentence what happens. If you cannot fully explain that (and if you don't think anything strange happens, you are wrong), don't usestd::bind
in generic code, as you don't understand how it works. If you can, wow, that is something I can't do. And you'll be qualified to correct me.std::bind
isn't bad, it has quirks, and nobody should bother learning those quirks. And if you don't know the quirks, you shouldn't use it. – Pantelegraphmethod_name(foo)
would work. They'd also do stuff with member function pointers and have been well vetted on the subject. – Pantelegraphfor_each(v, &Foo::bar);
does work out of the box. This question is currently lacking motivation. – Rafterstd::invoke
, but so can you! So why not give a concrete example, and give this question motivation? Right now anyone who reads it has to guess. – Rafterstd::mem_fn
, or usestd::invoke
and support a certain uniform "invokable" interface), but at the core language level it might be best to let them lie. – Flooded