将重载的成员函数传递给可变参数模板函数

时间:2014-10-17 13:59:49

标签: c++ c++11 variadic-templates

我有一个功能添加

的课程
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%清楚为什么我必须使用“&amp;”作为类成员函数,但如果我传入一个免费函数则没有&符号)。 无论如何,我也尝试了上面回答中提到的解决方法:

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

然后将MyClass :: test()修改为:

    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>()和/不带'&amp;'但它们都会产生一个或另一个编译器错误。

我想我还没有完全理解这个问题,如果有人能够解释它,我会很高兴。此外,我正在寻找这个问题的正确解决方案。实际情况并非如此,只要没有两个具有相同名称的函数,这只会起作用,并且一旦有,一切都会在没有正确的通用解决方案的情况下崩溃吗?

编辑:修正了一些拼写错误,并在此处上传了一个最小示例:http://ideone.com/eX1r1l

3 个答案:

答案 0 :(得分:2)

正如其他人所提到的,问题是doIt()doIt_wrapper类中不可调用,因为它还需要一个指向被调用对象的指针。 您可以修改doIt_wrapper operator()以获取指向对象的指针,并将指针作为第一个参数传递给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上编译。

答案 1 :(得分:1)

您可以使用lambda:

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是一种方法(不是自由函数或静态函数),因此您必须使用对象调用它。 如果您将static添加到doIt,则可以选择

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

答案 2 :(得分:1)

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

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

使用lamda表达式也可以。

此外,标准库函数std::async()已经完成了您在此处尝试执行的操作,以及更多功能。请考虑使用它。


编辑:您还需要转换为正确的类型以选择正确的重载。 @Jarod42已经向您展示了如何。