Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-130766

QtConcurrent::blockingMapped has incorrect argument deduction for generic lambdas

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P2: Important
    • 6.5.9, 6.8.2, 6.9.0 Beta2, 6.10.0 FF
    • 6.8.0
    • Core: QtConcurrent
    • None
    • Tested on Ubuntu 22.04 with GCC 12 and Windows with MSVC 2022
    • Linux/X11, Windows
    • 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.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            ivan.solovev Ivan Solovev
            phernst Philipp Ernst
            Vladimir Minenko Vladimir Minenko
            Alex Blasche Test Alex Blasche Test
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews