My question is about using std::function to class methods. Suppose I have the following class hierarchy:
class Foo {
public:
virtual void print() {
cout << "In Foo::print()" << endl;
}
virtual void print(int) {
cout << "In Foo::print(int)" << endl;
}
};
class Bar : public Foo {
public:
virtual void print() override {
cout << "In Bar::print()" << endl;
}
virtual void print(int) override {
cout << "In Bar::print(int)" << endl;
}
}
Now there is another function which is supposed to dynamically call one of the two class methods depends on its input:
void call(Foo* foo, void (Foo::*func)(void)) {
(foo->*func)();
}
Foo* foo = new Foo();
Bar* bar = new Bar();
call(foo, &Foo::print);
call(bar, &Foo::print);
When I compile the above code snippet using g++/clang++, it works as expected, where the output is:
In Foo::print()
In Bar::print()
My questions are then:
since there are two functions (overloaded) with the same name: print, when I pass the address of class function: &Foo::print
, how did the compiler know that I am actually calling Foo::print(void)
but not Foo::print(int)
?
is there another way that I can generalize the code above such that the second parameter of void call(Foo*, xxx)
can be passed using both Foo::print(void)
and Foo::print(int)
is there anyway to achieve this feature using new feature in C++11 std::function
? I understand that in order to use std::function
with a non-static class method, I have to use std::bind
to bind each class method with a specific class object, but that would be too inefficient for me because I have many class objects to be bound.
Since there are two functions (overloaded) with the same name:
&Foo::print
, how did the compiler knows that I am actually callingFoo::print(void)
but notFoo::print(int)
?
This is allowed because of [over.over]/p1:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set.
The compiler can use the target type of the parameter-type-list to determine which function from the overload set the pointer-to-member refers:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type is identical to the function type of the target type required in the context. [ Note: .. ] The target can be
— an object or reference being initialized (8.5, 8.5.3, 8.5.4),
— the left side of an assignment (5.18),
— a parameter of a function (5.2.2),
— [..]
The name Foo:print
represents an overload set which the compiler looks through to find a match. The target type Foo::print(void)
is present in the overload set, so the compiler resolves the name to that overload.
Is there another way that I can generalize the code above such that the second parameter of
void call(Foo*, xxx)
can be passed using bothFoo::print(void)
andFoo::print(int)
There isn't a general way to do it with the name itself. The name has to be resolved to an overload. Instead, try changing the code to accept a function object like a lambda:
template<class Callable>
void call(Foo* foo, Callable&& callback) {
callback(foo);
}
int main()
{
call(foo, [] (Foo* f) { f->print(); f->print(1); });
}