2017-08-02 16 views
1

當我嘗試運行此玩具示例時,出現不一致的參數包編譯器錯誤。有人可以解釋爲什麼'int a'在這裏被推斷爲int &?在下面的例子中,當我用int文字運行下面的'test'函數時,它工作正常。在此先感謝您的解釋!不一致的參數包扣除int和int&in可變模板成員函數創建一個運行成員函數的線程

class Test { 
    public: 
    Test() {} 

    ~Test() { 
     t.join(); 
    } 

    void print(int num) 
    { 
     std::cout << num << std::endl; 
    } 

    template<class ...Args> 
    void test(void(Test::*b)(Args...) , Args&&... args) 
    { 
     t = std::thread(b, this, std::forward<Args>(args)...); 
    } 

    std::thread t; 
    }; 

int main() 
{ 
    int a = 123; 
    Test test; 
    test.test(&Test::print, a); 
    // test.test(&Test::print, 123); works 
} 

錯誤:

prog.cc: In function 'int main()': 
prog.cc:82:40: error: no matching function for call to 'Test::test( 
void (Test::*)(int), int&)' 
    test.test(&Test::print, a); 
          ^ 
prog.cc:82:40: note: candidate is: 
prog.cc:62:10: note: template<class ... Args> void Test::test(void 
(Test::*)(Args ...), Args&& ...) 
    void test(void(Test::*b)(Args...) , Args&&... args) 
    ^
prog.cc:62:10: note: template argument deduction/substitution failed: 
prog.cc:82:40: note: inconsistent parameter pack deduction with 'int' and 
'int&' 
    test.test(&Test::print, a); 
          ^ 
+0

什麼是'printThree'? – Sergey

+0

@謝謝錯字 - 修正。抱歉。 – Mozbi

+0

您正在使用完美轉發。其中的重點是保存參數的值類別 - 推導左值參數的左值參考類型和右值參數的右值參考類型。看起來它正在完成工作,完美無缺。 –

回答

6

切勿使用推導向前引用類型精確匹配其他參數。

完美轉發Args當你通過一個左值int推導Argsint&。然後int& &&崩潰爲int&

總之,切勿使用推導的前向參考類型與其他參數完全匹配

有少數例外,但這是在庫樣式的代碼中,其中一種類型已經在另一種情況下從另一種類型推導出來。

此:

template<class ...Args> 
void test(void(Test::*b)(Args...) , Args&&... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

約束。

嘗試:

template<class F, class ...Args> 
void test(F&& f, Args&&... args) 
{ 
    t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...); 
} 

究竟是什麼第一個參數是不是你的問題。它可能是一個指向this的成員函數指針,它可能是一個可以將this作爲第一個參數的對象。

如果因任何原因,你要堅持,第一個參數是一個成員函數指針:

template<class R, class...A0s, class ...Args> 
void test(R(Test::*f)(A0s...), Args&&... args) 
{ 
    t = std::thread(f, this, std::forward<Args>(args)...); 
} 

不要過度約束它。如果你真的想確保誤差在test,而不是它的身體內invokation情況下,我們可以這樣做:

template<class R, class...A0s, class ...Args> 
auto test(R(Test::*f)(A0s...), Args&&... args) 
-> decltype((void)((std::declval<Test*>()->*f)(std::declval<typename std::decay<Args>::type>()...))> 
{ 
    t = std::thread(f, this, std::forward<Args>(args)...); 
} 

我們SFINAE禁用此基礎上能夠調用this->*fargs...腐爛副本。

這通常是矯枉過正。在這裏我們阻斷的論點推演,以及函數指針唯一模式匹配

template<class T> struct tag_t{using type=T;}; 
template<class T> using no_deduction=typename tag_t<T>::type; 

template<class ...Args> 
void test(void(Test::*b)(Args...) , no_deduction<Args>... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

最後,我們可以做到這一點。如果b想要參考,這不起作用;我們就需要一些額外類型的節目變身T&std::reference_wrapper<T>

template<class T> 
struct compatible_arg { using type=T; }; 
template<class T> 
struct compatible_arg<T&> { 
    using type=std::reference_wrapper<T>; 
}; 
template<class T> 
using compatible_arg_t = typename compatible_arg<T>::type; 

template<class ...Args> 
void test(void(Test::*b)(Args...) , compatible_arg_t<Args>... args) 
{ 
    t = std::thread(b, this, std::forward<decltype(args)>(args)...); 
} 

它映射到T&&T&&TTT&std::reference_wrapper<T>

不過說真的,只停留在:

template<class F, class ...Args> 
void test(F&& f, Args&&... args) 
{ 
    t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...); 
} 
+0

我想你可能重複了你的第一句話 –

+0

'std :: result_of'已被棄用。改爲使用[std :: invoke_result](http://en.cppreference.com/w/cpp/types/result_of)。 –

+0

@mario in [tag:C++ 11]? – Yakk

4
template<class ...Args> 
void test(void(Test::*b)(Args...) , Args&&... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

當你做這樣的事情,它意味着:

  • 推斷Args...從PMF作爲第一個參數傳遞。
  • 然後獨立於其餘參數的類型和值類別推導出Args...
  • 兩次獨立扣除的結果必須匹配,否則就是錯誤。

這實際上是從來沒有你實際上想要做什麼。函數參數的類型與其相應參數的類型和值類別之間通常沒有精確匹配關係。

在這裏,你並不需要PMF的參數類型(更不用說你必須寫一個gazillion重載來覆蓋cv-和ref-qualifiers的所有可能的組合),所以你可以將它限制爲「指針的某種類型的Test成員「:

template<class F, class... Args> 
void test(F Test::* b, Args&&... args) 
{ 
    t = std::thread(b, this, std::forward<Args>(args)...); 
} 

或者乾脆把它不受約束:

template<class F, class... Args> 
void test(F&& f, Args&&... args) 
{ 
    t = std::thread(std::forward<F>(f), this, std::forward<Args>(args)...); 
} 

或者,你可以引入一個新的包:

template<class ...Args, class... Args2> 
void test(void(Test::*b)(Args...) , Args2&&... args) 
{ 
    t = std::thread(b, this, std::forward<Args2>(args)...); 
} 
+0

感謝您的迴應。對於引入新包的第三個示例,是否根據b的函數簽名來推導出第一包(Args)? – Mozbi

+0

@Mozbi,[是](http://coliru.stacked-crooked.com/a/ff636a54268967d4)。 –