2014-10-17 95 views
0

我有一类函数添加通重载成员函数的可变参数模板功能

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"); }; 

    void test() { 
     Pool myPool; 
     string a("test"); 
     myPool.add(&MyClass::doIt, a); // Error 
    }; 
}; 

其中给出一个编译器错误:

Error 1 error C2914: 'Pool::add' : cannot deduce template argument as function argument is ambiguous MyClass.cpp 94 

现在的问题是(我认为),编译器不能推断出我超载想用。类似于Overloaded function as argument of variadic template function。我也不是100%清楚为什么我不得不使用“&”作为类成员函数,但是如果我传入一个自由函数,则不需要使用&符号)。 反正我也试过在上述答复中提到的解决方法:

struct doIt_wrapper { 
    template <typename... T> 
    auto operator()(T... args) -> decltype(doIt(args...)) { 
     return doIt(args...); 
    } 
}; 

,然后修改MyClass的::测试()来:

void test() { 
     Pool myPool; 
     string a("test"); 
     myPool.add(doIt_wrapper(), a); 
    }; 

但它也给了我一个编译器错误:

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 

我也尝试了一些变体,如myPool.add(doIt_wrapper<string>()和有/没有'&',但它们都会产生一个或另一个编译器错误。

我觉得我还没有完全理解这个问题,如果有人能说清楚,我会很高兴。此外,我正在寻找适当的解决方案来解决这个问题。事实并非如此,只要没有两个具有相同名称的函数,只有这样,所有事情都会崩溃,而没有适当的通用解决方案?

编辑:修正了一些错别字,并上载一个小例子,在这里:http://ideone.com/eX1r1l

回答

1

你可以使用拉姆达:

myPool.add([this](const std::string& s) {doIt(s);}, a); 

甚至

myPool.add([this, a]() {doIt(a);}); 

目前,您可以指示过载以这种方式使用:

myPool.add(static_cast<std::string (MyClass::*) (std::string)>(&MyClass::doIt), a); 

请注意,doIt是一种方法(不是自由函数或静态函数),因此您必须用对象调用它。 如果添加staticdoIt,你可以选择过载与

myPool.add(static_cast<std::string (*) (std::string)>(&MyClass::doIt), a); 
+0

第一次演员还需要绑定'this'。 – 0x499602D2 2014-10-17 15:10:41

+0

谢谢你的lambda和铸造解决方案!他们确实都工作。现在就必须决定哪个是最不丑陋的,或者更确切地说,它以最清晰的方式表现出意图。 – Ela782 2014-10-17 19:51:06

+0

@ Ela782:lambda的第二种方式对我来说似乎最清晰(您可能更喜欢'[=]'捕获)。 – Jarod42 2014-10-17 20:15:14

1

的问题是,非静态成员函数有一个隐藏的,隐含的this参数指向调用它的实例。您的编译器有权拒绝它,因为它没有该函数的正确参数。在this发送作为附加绑定参数将工作:

myPool.add(&MyClass::doIt, this, a); 
//       ^^^^ 

使用的λ表达式将正常工作。

此外,标准库函数std::async()已经做了你在这里试图做的,等等。考虑使用它来代替。


编辑:您还需要转换为正确的类型来选择正确的过载。 @Jarod42已经告诉你如何。

+0

添加'this'不会解决*过载问题*。 – Jarod42 2014-10-17 14:50:17

+0

@ Jarod42谢谢。 – 0x499602D2 2014-10-17 15:08:07

+0

啊,我不知道那隐藏的,隐含的'this'参数!非常感谢你!感谢你指出std :: async--当然我已经知道它,但它不完全与线程池相同 - 如果我调用异步1000次,它可能产生1000个线程,同时使用线程池,一次只能运行x个线程(例如x = 8),抓取正在工作的线程。 – Ela782 2014-10-17 19:46:31

2

正如其他人所说的,问题是doIt()不能在doIt_wrapper类中调用,因为它还需要一个指向所调用对象的指针。 您可以修改doIt_wrapperoperator()以获取指向该对象的指针,并将指针传递给this作为add()的第一个参数。 那么这将是这个样子:

#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; 
} 

这样你就不必做转换。代码在GCC和Clang上编译。

+0

非常感谢您指出此解决方案并纠正我的包装代码!很高兴知道如何实施该选项。虽然我倾向于其他解决方案。 – Ela782 2014-10-17 19:52:48

相关问题