Details
-
Bug
-
Resolution: Fixed
-
P2: Important
-
6.8.0
-
None
-
Tested on Ubuntu 22.04 with GCC 12 and Windows with MSVC 2022
-
-
3
-
f73765682 (dev), 7b356f0f3 (6.9), 60f945527 (6.8), 61fffbea6 (tqtc/lts-6.5)
-
Foundation Sprint 122
Description
QtConcurrent::blockingMapped does not (always) correctly deduce the argument type if the map functor is a generic lambda. This code fails to compile:
#include <QList> #include <QtConcurrentMap> #include <string> int main(){ static const auto toString = [](const auto& element) { return std::to_string(element); }; std::ignore = QtConcurrent::blockingMapped(QList<int>{}, toString); }
With this output from gcc:
[1/2] Building CXX object CMakeFiles/qtblockingmappeddeduction.dir/main.cpp.o FAILED: CMakeFiles/qtblockingmappeddeduction.dir/main.cpp.o /usr/bin/g++ -DQT_CONCURRENT_LIB -DQT_CORE_LIB -isystem /opt/Qt/6.8.0/gcc_64/include/QtCore -isystem /opt/Qt/6.8.0/gcc_64/include -isystem /opt/Qt/6.8.0/gcc_64/mkspecs/linux-g++ -isystem /opt/Qt/6.8.0/gcc_64/include/QtConcurrent -g -fPIC -MD -MT CMakeFiles/qtblockingmappeddeduction.dir/main.cpp.o -MF CMakeFiles/qtblockingmappeddeduction.dir/main.cpp.o.d -o CMakeFiles/qtblockingmappeddeduction.dir/main.cpp.o -c /home/phernst/qtblockingmappeddeduction/main.cpp /home/phernst/qtblockingmappeddeduction/main.cpp: In instantiation of 'main()::<lambda(const auto:40&)> [with auto:40 = std::__cxx11::basic_string<char>]': /usr/include/c++/12/type_traits:2559:26: required by substitution of 'template<class _Fn, class ... _Args> static std::__result_of_success<decltype (declval<_Fn>()((declval<_Args>)()...)), std::__invoke_other> std::__result_of_other_impl::_S_test(int) [with _Fn = const main()::<lambda(const auto:40&)>&; _Args = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >}]' /usr/include/c++/12/type_traits:2570:55: required from 'struct std::__result_of_impl<false, false, const main()::<lambda(const auto:40&)>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >' /usr/include/c++/12/type_traits:3045:12: recursively required by substitution of 'template<class _Result, class _Ret> struct std::__is_invocable_impl<_Result, _Ret, true, std::__void_t<typename _CTp::type> > [with _Result = std::__invoke_result<const main()::<lambda(const auto:40&)>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >; _Ret = void]' /usr/include/c++/12/type_traits:3045:12: required from 'struct std::is_invocable<const main()::<lambda(const auto:40&)>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >' /opt/Qt/6.8.0/gcc_64/include/QtConcurrent/qtconcurrentmap.h:156:88: required by substitution of 'template<class Sequence, class MapFunctor, class ReduceFunctor, class InitialValueType, typename std::enable_if<std::is_invocable<MapFunctor, typename std::decay<_Tp>::type::value_type>::value, int>::type <anonymous>, class ResultType, typename std::enable_if<isInitialValueCompatible_v<InitialValueType, ResultType>, int>::type <anonymous> > QFuture<ResultType> QtConcurrent::mappedReduced(QThreadPool*, Sequence&&, MapFunctor&&, ReduceFunctor&&, InitialValueType&&, ReduceOptions) [with Sequence = QList<std::__cxx11::basic_string<char> >; MapFunctor = const main()::<lambda(const auto:40&)>&; ReduceFunctor = QtPrivate::PushBackWrapper; InitialValueType = QFlags<QtConcurrent::ReduceOption>&; typename std::enable_if<std::is_invocable<MapFunctor, typename std::decay<_Tp>::type::value_type>::value, int>::type <anonymous> = <missing>; ResultType = <missing>; typename std::enable_if<isInitialValueCompatible_v<InitialValueType, ResultType>, int>::type <anonymous> = <missing>]' /opt/Qt/6.8.0/gcc_64/include/QtConcurrent/qtconcurrentmap.h:436:38: required from 'ResultType QtConcurrent::blockingMappedReduced(QThreadPool*, Sequence&&, MapFunctor&&, ReduceFunctor&&, ReduceOptions) [with ResultType = QList<std::__cxx11::basic_string<char> >; Sequence = QList<int>; MapFunctor = const main()::<lambda(const auto:40&)>&; ReduceFunctor = QtPrivate::PushBackWrapper; ReduceOptions = QFlags<ReduceOption>]' /opt/Qt/6.8.0/gcc_64/include/QtConcurrent/qtconcurrentmap.h:777:49: required from 'auto QtConcurrent::blockingMapped(InputSequence&&, MapFunctor&&) [with MapFunctor = const main()::<lambda(const auto:40&)>&; InputSequence = QList<int>]' /home/phernst/qtblockingmappeddeduction/main.cpp:12:47: required from here /home/phernst/qtblockingmappeddeduction/main.cpp:9:30: error: no matching function for call to 'to_string(const std::__cxx11::basic_string<char>&)' 9 | return std::to_string(element); | ~~~~~~~~~~~~~~^~~~~~~~~ In file included from /usr/include/c++/12/string:53, from /usr/include/c++/12/bits/locale_classes.h:40, from /usr/include/c++/12/bits/ios_base.h:41, from /usr/include/c++/12/streambuf:41, from /usr/include/c++/12/bits/streambuf_iterator.h:35, from /usr/include/c++/12/iterator:66, from /opt/Qt/6.8.0/gcc_64/include/QtCore/qcontainertools_impl.h:20, from /opt/Qt/6.8.0/gcc_64/include/QtCore/qarraydataops.h:9, from /opt/Qt/6.8.0/gcc_64/include/QtCore/qarraydatapointer.h:7, from /opt/Qt/6.8.0/gcc_64/include/QtCore/qlist.h:8, from /opt/Qt/6.8.0/gcc_64/include/QtCore/QList:1, from /home/phernst/qtblockingmappeddeduction/main.cpp:1: /usr/include/c++/12/bits/basic_string.h:4020:3: note: candidate: 'std::string std::__cxx11::to_string(int)' 4020 | to_string(int __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4020:17: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'int' 4020 | to_string(int __val) | ~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4034:3: note: candidate: 'std::string std::__cxx11::to_string(unsigned int)' 4034 | to_string(unsigned __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4034:22: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'unsigned int' 4034 | to_string(unsigned __val) | ~~~~~~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4045:3: note: candidate: 'std::string std::__cxx11::to_string(long int)' 4045 | to_string(long __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4045:18: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'long int' 4045 | to_string(long __val) | ~~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4059:3: note: candidate: 'std::string std::__cxx11::to_string(long unsigned int)' 4059 | to_string(unsigned long __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4059:27: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'long unsigned int' 4059 | to_string(unsigned long __val) | ~~~~~~~~~~~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4070:3: note: candidate: 'std::string std::__cxx11::to_string(long long int)' 4070 | to_string(long long __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4070:23: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'long long int' 4070 | to_string(long long __val) | ~~~~~~~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4082:3: note: candidate: 'std::string std::__cxx11::to_string(long long unsigned int)' 4082 | to_string(unsigned long long __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4082:32: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'long long unsigned int' 4082 | to_string(unsigned long long __val) | ~~~~~~~~~~~~~~~~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4093:3: note: candidate: 'std::string std::__cxx11::to_string(float)' 4093 | to_string(float __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4093:19: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'float' 4093 | to_string(float __val) | ~~~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4102:3: note: candidate: 'std::string std::__cxx11::to_string(double)' 4102 | to_string(double __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4102:20: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'double' 4102 | to_string(double __val) | ~~~~~~~^~~~~ /usr/include/c++/12/bits/basic_string.h:4111:3: note: candidate: 'std::string std::__cxx11::to_string(long double)' 4111 | to_string(long double __val) | ^~~~~~~~~ /usr/include/c++/12/bits/basic_string.h:4111:25: note: no known conversion for argument 1 from 'const std::__cxx11::basic_string<char>' to 'long double' 4111 | to_string(long double __val) | ~~~~~~~~~~~~^~~~~ ninja: build stopped: subcommand failed.
Which suggests the argument type has been deduced to be `std::string` instead of `int`.
It does work if the lambda is not generic, i.e.
[](const int& element) { ... }
Or if the return type is explicitly specified, i.e.
[](const auto& element) -> std::string { ... }
The error does not occur with QtConcurrent::mapped.
It also didn't occur with Qt 6.2.4.