2017-08-02 38 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)。 –