【问题标题】:Compilation error when a thread invokes a function template (for a default argument)线程调用函数模板时的编译错误(对于默认参数)
【发布时间】:2018-04-21 16:37:10
【问题描述】:

以下函数模板来自 Stroustrup 的书(“CPL”,第 4 版):

template<typename Arg1, typename Arg2, typename Arg3>
void write(Arg1 a1, Arg2 a2 = {}, Arg3 a3 = {});

template<typename Arg1, typename Arg2, typename Arg3>
void write(Arg1 a1, Arg2 a2, Arg3 a3)
{
   thread::id name = this_thread::get_id();

   coutm.lock();

   cout << "From thread " << name << " : "
        << a1 << ' ' << a2 << ' ' << a3
        << endl;

   coutm.unlock();
}


我正在尝试执行以下操作here:
1) 调用此函数仅传递 2 个参数。第三个参数应该是默认的。
2) 函数模板调用直接传递给线程(而不是通过 lambda)。

int main()
{
   thread t (write<char, float>, 'a', 4.9);

   t.join();
}


但是,我收到以下编译错误:

main.cpp: In function 'int main()':
main.cpp:22:42: error: no matching function for call to 'std::thread::thread(<unresolved overloaded function type>, char, double)'

    thread t (write<char, float>, 'a', 4.9);
                                          ^
In file included from main.cpp:7:0:
/usr/local/include/c++/7.2.0/thread:118:7: note: candidate: template<class _Callable, class ... _Args> std::thread::thread(_Callable&&, _Args&& ...)

       thread(_Callable&& __f, _Args&&... __args)
       ^~~~~~
/usr/local/include/c++/7.2.0/thread:118:7: note:   template argument deduction/substitution failed:
main.cpp:22:42: note:   couldn't deduce template parameter '_Callable'

    thread t (write<char, float>, 'a', 4.9);
                                          ^
In file included from main.cpp:7:0:
/usr/local/include/c++/7.2.0/thread:113:5: note: candidate: std::thread::thread(std::thread&&)

     thread(thread&& __t) noexcept
     ^~~~~~

/usr/local/include/c++/7.2.0/thread:113:5: note:   candidate expects 1 argument, 3 provided
/usr/local/include/c++/7.2.0/thread:106:5: note: candidate: std::thread::thread()

     thread() noexcept = default;
     ^~~~~~

/usr/local/include/c++/7.2.0/thread:106:5: note:   candidate expects 0 arguments, 3 provided


对于默认参数,是否可以通过线程直接调用这样的函数模板?如果有怎么办?否则,必须将 lambda 传递给线程构造函数吗?

【问题讨论】:

  • 11.3.6 似乎建议稍后添加默认参数仅适用于非模板函数。不知道反过来是否适用。 Stroustrup 的示例没有初始声明,而是将默认值放在定义中。

标签: c++ multithreading language-lawyer


【解决方案1】:

不能从默认(函数)参数推导出模板参数。您没有明确指定Arg3,因此无法实例化write。换句话说,您希望a3 在线程对write 的调用中是什么类型

【讨论】:

  • 好点,但 Ripi2 的修复 - 使用默认类型 - 应该有效。但事实并非如此。
  • @SSteven:我认为这里可能存在实现错误:我在thread.thread.constr 或 [func.require] 中看不到任何提示参数计数和类型的内容(这里,double 参数float 参数)必须比普通调用匹配更多
【解决方案2】:
template<typename Arg1, typename Arg2, typename Arg3>
void write(Arg1 a1, Arg2 a2 = {}, Arg3 a3 = {});
......
thread t (write<char, float>, 'a', 4.9);

仅传递 2 个参数调用此函数。第三个论据 应该默认。

如果编译器知道它的类型,它会使用默认参数,这不是你的情况。


第一个解决方法是给write声明一个默认的参数类型

template<typename Arg1, typename Arg2, typename Arg3 = int> //default Arg3 type
void write(Arg1 a1, Arg2 a2 = {}, Arg3 a3 = {});

这仍然会产生错误,嗯,新的错误。我正在使用来自Compiler Explorer的 g++ 7.3、clang 6.0.0 和 icc 18.0.0 进行测试

有趣的是,g++ 允许默认类型typename Arg3 = int,或与声明不同的类型,也在定义中(在下一次尝试中,见下文),但不仅在定义中。除了逻辑的“在声明中”之外,Clang 和 icc 在任何情况下都会失败。

尝试修复,但没有线程:

int main()
{
   //thread t (write<char, float>, 'a', 4.9);
   write<char, float>('a', 4.9);

   //t.join();
}

啊哈。现在它使用 g++ 7.3、clang 6.0.0 和 icc 18.0.0 编译并输出 a 4.9 0


那么问题出在std::thread constructor 为什么?

icc 编译器期望函数参数的数量与模板推导后的这些参数的数量相同,无论它们是否具有默认值(即= {}):

error: static assertion failed with "Wrong number of arguments for function"
        static_assert(sizeof...(_BoundArgs) == sizeof...(_Args),
        ^

          detected during:
            instantiation of class "std::_Bind_check_arity<_Ret (*)(_Args...), _BoundArgs...> [with _Ret=void, _Args=<char, float, int>, _BoundArgs=<char, double>]"  
            instantiation of class "std::_Bind_simple_helper<_Func, _BoundArgs...> [with _Func=void (&)(char, float, int), _BoundArgs=<char, double>]"
            instantiation of "std::thread::thread(_Callable &&, _Args &&...) [with _Callable=void (&)(char, float, int), _Args=<char, double>]" 

clang 错误:

 error: no matching member function for call to '_M_invoke'
        -> decltype(std::declval<_Invoker&>()._M_invoke(_Indices()))
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~

 note: in instantiation of template class 'std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >' requested here
         __make_invoker(std::forward<_Callable>(__f),

gcc 错误:

In instantiation of 'struct std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >':
required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(char, float, int); _Args = {char, double}]'
required from here
error: no matching function for call to 'std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >::_M_invoke(std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >::_Indices)'
  operator()()
  ^~~~~~~~

【讨论】:

  • 如果您没有将写入函数模板传递给线程,则无需显式指定模板参数。类型将被推断:write('a', 4.9); 可以工作。
猜你喜欢
  • 2019-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多