我正在尝试将QtConcurrent::mapped
用于QVector<QString>
。我已经尝试了很多方法,但似乎总是存在重载问题。
QVector<QString> words = {"one", "two", "three", "four"};
using StrDouble = std::pair<QString, double>;
QFuture<StrDouble> result = QtConcurrent::mapped<StrDouble>(words, [](const QString& word) -> StrDouble {
return std::make_pair(word + word, 10);
});
此代码段返回以下错误:
/home/lhahn/dev/cpp/TestLambdaConcurrent/mainwindow.cpp:23: error: no matching function for call to ‘mapped(QVector<QString>&, MainWindow::MainWindow(QWidget*)::<lambda(const QString&)>)’
});
^
我看到了这个post,它说Qt找不到lambda的返回值,所以你必须使用std::bind
。如果我试试这个:
using StrDouble = std::pair<QString, double>;
using std::placeholders::_1;
auto map_fn = [](const QString& word) -> StrDouble {
return std::make_pair(word + word, 10.0);
};
auto wrapper_map_fn = std::bind(map_fn, _1);
QFuture<StrDouble> result = QtConcurrent::mapped<StrDouble>(words, wrapper_map_fn);
但错误仍然类似:
/home/lhahn/dev/cpp/TestLambdaConcurrent/mainwindow.cpp:28: error: no matching function for call to ‘mapped(QVector<QString>&, std::_Bind<MainWindow::MainWindow(QWidget*)::<lambda(const QString&)>(std::_Placeholder<1>)>&)’
QFuture<StrDouble> result = QtConcurrent::mapped<StrDouble>(words, wrapper_map_fn);
^
我也尝试将lambda包装在std::function
内但不幸的是结果相似。
答案 0 :(得分:3)
以下编译:
QVector<QString> words = {"one", "two", "three", "four"};
std::function<StrDouble(const QString& word)> func = [](const QString &word) {
return std::make_pair(word + word, 10.0);
};
QFuture<StrDouble> result = QtConcurrent::mapped(words, func);
qDebug() << result.results()
的输出:
(std :: pair(“oneone”,10),std :: pair(“twotwo”,10),std :: pair(“threethree”,10),std :: pair(“fourfour”,10 ))
答案 1 :(得分:1)
不幸的是,QtConcurrent :: mapped不支持带捕获的lambda函数。您可能需要自定义实现。例如,您可以使用AsyncFuture创建一个:
template <typename T, typename Sequence, typename Functor>
QFuture<T> mapped(Sequence input, Functor func){
auto defer = AsyncFuture::deferred<T>();
QList<QFuture<T>> futures;
auto combinator = AsyncFuture::combine();
for (int i = 0 ; i < input.size() ; i++) {
auto future = QtConcurrent::run(func, input[i]);
combinator << future;
futures << future;
}
AsyncFuture::observe(combinator.future()).subscribe([=]() {
QList<T> res;
for (int i = 0 ; i < futures.size(); i++) {
res << futures[i].result();
}
auto d = defer;
d.complete(res);
});
return defer.future();
}
用法:
auto future = mapped<int>(input, func);
完整示例:
https://github.com/benlau/asyncfuture/blob/master/tests/asyncfutureunittests/example.cpp#L326
答案 2 :(得分:0)
QtConcurrent::map[ped]
适用于具有result_type
成员类型的仿函数类型。因此,您需要将lambda包装在提供此类型的类中。 std::function
包装器提供了这个,但它可能有更多的开销 - 因此我们可以创建自己的。
#include <utility>
#include <type_traits>
template <class T> struct function_traits : function_traits<decltype(&T::operator())> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
// specialization for pointers to member function
using functor_type = ClassType;
using result_type = ReturnType;
using arg_tuple = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
};
template <class Callable, class... Args>
struct CallableWrapper : Callable, function_traits<Callable> {
CallableWrapper(const Callable &f) : Callable(f) {}
CallableWrapper(Callable &&f) : Callable(std::move(f)) {}
};
template <class F, std::size_t ... Is, class T>
auto wrap_impl(F &&f, std::index_sequence<Is...>, T) {
return CallableWrapper<F, typename T::result_type,
std::tuple_element_t<Is, typename T::arg_tuple>...>(std::forward<F>(f));
}
template <class F> auto wrap(F &&f) {
using traits = function_traits<F>;
return wrap_impl(std::forward<F>(f),
std::make_index_sequence<traits::arity>{}, traits{});
}
除了Qt所需的result_type
之外,包装的仿函数还包含functor_type
,arg_tuple
和arity
。
不是直接传递lambda,而是传递包装的仿函数:
auto result = QtConcurrent::mapped<StrDouble>(words, wrap([](const QString& word){
return std::make_pair(word + word, 10);
}));
wrap
返回的值是一个实现result_type
的算符。