为何GCC/Clang下packaged_task模板参数推导失败?如何修复?
问题描述
以下代码在Windows平台MSVC 2022编译器、C++17模式下可正常编译,但在Linux平台GCC 11.3和Clang 16.0编译器中编译失败。
测试代码
#include <vector> #include <future> #include <numeric> #include <thread> using namespace std; template <class T> T parallel_accumulate(const std::vector<T>& v) { packaged_task pt0{accumulate<const T*, T>}; packaged_task pt1{accumulate<const T*, T>}; future f0{pt0.get_future()}; future f1{pt1.get_future()}; thread t0{move(pt0), &v[0], &v[v.size() / 2], 0}; thread t1{move(pt1), &v[v.size() / 2], &v[0] + v.size(), 0}; t0.join(); t1.join(); return f0.get() + f1.get(); } #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include <doctest/doctest.h> TEST_CASE("accumulate") { constexpr size_t checks_count = 10; std::vector<int> v; v.reserve(checks_count); for (size_t i = 1; i <= checks_count; ++i) { v.push_back(i); CHECK(parallel_accumulate(v) == i * (i + 1) / 2); } }
GCC编译错误信息
/home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:39:5: required from here /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:11:17: error: class template argument deduction failed: 11 | packaged_task pt0{accumulate<const T*, T>}; | ^~~ /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:11:17: error: no matching function for call to ‘packaged_task(<unresolved overloaded function type>)’ In file included from /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:2: /usr/include/c++/11/future:130:11: note: candidate: ‘template<class _Signature> packaged_task()-> std::packaged_task<_Signature>’ 130 | class packaged_task; | ^~~~~~~~~~~~~ /usr/include/c++/11/future:130:11: note: template argument deduction/substitution failed: /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:11:17: note: candidate expects 0 arguments, 1 provided 11 | packaged_task pt0{accumulate<const T*, T>}; | ^~~ In file included from /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:2: /usr/include/c++/11/future:130:11: note: candidate: ‘template<class _Signature> packaged_task(std::packaged_task<_Signature>)-> std::packaged_task<_Signature>’ 130 | class packaged_task; | ^~~~~~~~~~~~~ /usr/include/c++/11/future:130:11: note: template argument deduction/substitution failed: /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:11:17: note: mismatched types ‘std::packaged_task<_Signature>’ and ‘int (*)(const int*, const int*, int)’ 11 | packaged_task pt0{accumulate<const T*, T>}; | ^~~ /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:11:17: note: couldn’t deduce template parameter ‘_Signature’
Clang编译错误信息
/home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:11:17: error: no viable constructor or deduction guide for deduction of template arguments of 'packaged_task' packaged_task pt0{accumulate<const T*, T>}; ^ /home/bobeff/projects/cpp/cpp_programming_language/05_a_tour_of_cpp_concurrency_and_utilities/packaged_task.cpp:39:11: note: in instantiation of function template specialization 'parallel_accumulate<int>' requested here CHECK(parallel_accumulate(v) == i * (i + 1) / 2); ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/future:130:11: note: candidate template ignored: could not match 'packaged_task<_Signature>' against 'int (*)(const int *, const int *, int)' class packaged_task; ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/future:130:11: note: candidate function template not viable: requires 0 arguments, but 1 was provided
原因分析
- C++17标准中,
std::packaged_task并未提供从函数指针自动推导模板签名参数的推导指南。 - MSVC 2022实现了非标准扩展,允许直接从函数指针推导
packaged_task的模板参数;而GCC和Clang严格遵循C++标准,未实现该扩展,因此无法推导_Signature模板参数,导致编译失败。
修复方案
以下两种方式均可实现跨编译器兼容:
方式一:显式指定packaged_task的模板签名
明确指定packaged_task的函数签名格式(返回类型(参数类型列表)),让编译器无需推导:
template <class T> T parallel_accumulate(const std::vector<T>& v) { // 显式指定签名:T(const T*, const T*, T) packaged_task<T(const T*, const T*, T)> pt0{accumulate<const T*, T>}; packaged_task<T(const T*, const T*, T)> pt1{accumulate<const T*, T>}; future<T> f0{pt0.get_future()}; future<T> f1{pt1.get_future()}; thread t0{move(pt0), &v[0], &v[v.size() / 2], T{0}}; thread t1{move(pt1), &v[v.size() / 2], &v[0] + v.size(), T{0}}; t0.join(); t1.join(); return f0.get() + f1.get(); }
方式二:用lambda包裹调用(更简洁)
通过lambda包裹accumulate调用,利用C++17对lambda的推导支持,让编译器自动推导packaged_task的模板参数:
template <class T> T parallel_accumulate(const std::vector<T>& v) { packaged_task pt0{[](const T* first, const T* last, T init) { return accumulate(first, last, init); }}; packaged_task pt1{[](const T* first, const T* last, T init) { return accumulate(first, last, init); }}; auto f0 = pt0.get_future(); auto f1 = pt1.get_future(); thread t0{move(pt0), &v[0], &v[v.size() / 2], T{0}}; thread t1{move(pt1), &v[v.size() / 2], &v[0] + v.size(), T{0}}; t0.join(); t1.join(); return f0.get() + f1.get(); }
内容的提问来源于stack exchange,提问作者bobeff
相关产品推荐
相关产品推荐

