Reputation: 10528
Please consider the following template function, that takes a callable, evaluates it, and returns the result (just for illustrative purposes):
template<typename F, typename... A>
auto evaluate(F&& f, A&&... args) -> decltype(f(std::forward<A>(args)...))
{
return f(args...);
}
This works for free-standing functions, but it breaks when passing member functions as follows for example, where foo
is an instance of Foo
:
evaluate(&Foo::bar, foo, ...);
It complains about not being able to call the member function:
error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘f (...)’,
e.g. ‘(... ->* f) (...)’ auto evaluate(F&& f, A&&... args) -> decltype(f(std::forward<A>(args)...))
Wrapping f
in std::ref
does allow passing member functions:
template<typename F, typename... A>
auto evaluate(F&& f, A&&... args) -> decltype(std::ref(f)(std::forward<A>(args)...))
...
Why does this work?
Upvotes: 0
Views: 97
Reputation: 69902
The functionality of making a reference_wrapper callable if the wrapped object is callable is fundamental to the ability to pass references to function objects into standard algorithms and the like.
here we create a tuple of references to function object:
int main()
{
struct A {
void operator()() const {
std::cout << "A\n";
}
};
struct B {
void operator()() const {
std::cout << "B\n";
}
};
A a;
B b;
auto ab = std::tie(a, b);
std::get<0>(ab)();
std::get<1>(ab)();
}
And here we pass a reference to a stateful function object into a standard algorithm:
struct EqualAndCount
{
EqualAndCount(char sought) : sought_(sought) {}
template<class R>
bool operator()(R const& r)
{
counter_++;
return sought_ == r;
}
std::size_t counter_ = 0;
char sought_;
};
int main()
{
EqualAndCount eq('j');
auto s = std::string("abcdefghijklmnop");
auto i = std::find_if(s.begin(), s.end(), std::ref(eq));
std::cout << "searched " << eq.counter_ << " chars";
if (i == s.end())
std::cout << " and did not find it\n";
else
std::cout << " and found it\n";
}
expected output:
searched 10 chars and found it
Why does this work?
Because std::reference_wrapper::operator()
is written in terms of the mythical INVOKE
(up to c++14) and in terms of std::invoke
(c++17).
documentation here:
http://en.cppreference.com/w/cpp/utility/functional/invoke
Upvotes: 2