Reputation: 5181
I have a class with a function add:
class Pool {
public:
Pool() {};
template<class F, class... A>
auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F(A...)>::type>
{
// return empty placeholder, for the sake of this code example
std::future<typename std::result_of<F(A...)>::type> ret;
return ret;
};
};
It should take any function with its arguments, add it to a thread pool and return a future of the result type of that function.
And a class where I use it:
class MyClass {
public:
string doIt(string) { return string("a"); };
string doIt(int, string) { return string("b"); };
void test() {
Pool myPool;
string a("test");
myPool.add(&MyClass::doIt, a); // Error
};
};
Which gives a compiler error:
Error 1 error C2914: 'Pool::add' : cannot deduce template argument as function argument is ambiguous MyClass.cpp 94
Now the problem is (I think) that the compiler can't deduce which overload I want to use. Similar to Overloaded function as argument of variadic template function. (Also I'm not 100% clear on why I have to use "&" for class member functions, but no ampersand if I pass in a free function). Anyway I also tried the workaround mentioned in above answer:
struct doIt_wrapper {
template <typename... T>
auto operator()(T... args) -> decltype(doIt(args...)) {
return doIt(args...);
}
};
and then modifying MyClass::test() to:
void test() {
Pool myPool;
string a("test");
myPool.add(doIt_wrapper(), a);
};
But it also gives me a compiler error:
error C2893: Failed to specialize function template 'unknown-type doIt_wrapper::operator ()(T...)' C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xrefwrap 58
I also tried a few variants like myPool.add(doIt_wrapper<string>()
and with/without '&' but they all generate one or the other compiler error.
I think I don't fully understand the problem yet and I would be glad if someone could shed light on it. Also I am looking for a proper solution to this problem. It can't really be the case that this only works as long as there are no two functions with the same name, and as soon as there are, everything breaks down without a proper, generic solution?
Edit: Fixed a few typos and uploaded a minimal example here: http://ideone.com/eX1r1l
Upvotes: 0
Views: 526
Reputation: 217275
You may use lambda:
myPool.add([this](const std::string& s) {doIt(s);}, a);
or even
myPool.add([this, a]() {doIt(a);});
Currently, you may indicate which overload to use that way:
myPool.add(static_cast<std::string (MyClass::*) (std::string)>(&MyClass::doIt), a);
Note that doIt
is a method (not a free function or static function), so you have to call it with an object.
If you add static
to doIt
, you may choose the overload with
myPool.add(static_cast<std::string (*) (std::string)>(&MyClass::doIt), a);
Upvotes: 1
Reputation: 444
As others have mentioned, the problem is that doIt()
is not callable inside the doIt_wrapper
class as it also needs a pointer to the object called on.
You could just modify the doIt_wrapper
operator()
to also take a pointer to the object and pass a pointer to this
as first argument to add()
.
It would then look something like this:
#include <iostream>
#include <future>
using namespace std;
class Pool {
public:
Pool() {};
template<class F, class... A>
auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F&&(A&&...)>::type>
{
// return empty placeholder, for the sake of this code example
std::future<typename std::result_of<F&&(A&&...)>::type> ret;
return ret;
};
};
class MyClass {
public:
string doIt(string) { return string("a"); };
string doIt(int, string) { return string("b"); };
struct doIt_wrapper
{
template<class T, class... Ts>
auto operator()(T&& t, Ts&&... args) -> decltype(t->doIt(std::forward<Ts>(args)...))
{
return t->doIt(std::forward<Ts>(args)...);
}
};
void test() {
Pool myPool;
string a("test");
myPool.add(doIt_wrapper(), this, a); // No error no more
};
};
int main() {
// your code goes here
MyClass my;
my.test();
return 0;
}
This way you don't have to do the casts. The code compiles on both GCC and Clang.
Upvotes: 2
Reputation: 96810
The problem is that non-static member functions have a hidden, implicit this
parameter that points to the instance that called it. Your compiler is right to reject it as it doesn't have the correct arguments for the function. Sending in this
as an additional bound parameter will work:
myPool.add(&MyClass::doIt, this, a);
// ^^^^
Using a lamda expression will work as well.
Also, the standard library function std::async()
already does what you're trying to do here, and more. Consider using that instead.
Edit: You also need to cast to the correct type to select the correct overload. @Jarod42 already shows you how.
Upvotes: 1