2014-10-17 99 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