c++ - 完美转发到异步 lambda

标签 c++ c++11 lambda perfect-forwarding stdasync

我有一个函数模板,我想在其中完美转发到我在另一个线程上运行的 lambda。这是您可以直接编译的最小测试用例:

#include <thread>
#include <future>
#include <utility>
#include <iostream>
#include <vector>

/**
 * Function template that does perfect forwarding to a lambda inside an
 * async call (or at least tries to). I want both instantiations of the
 * function to work (one for lvalue references T&, and rvalue reference T&&).
 * However, I cannot get the code to compile when calling it with an lvalue.
 * See main() below.
 */
template <typename T>
std::string accessValueAsync(T&& obj)
{

    std::future<std::string> fut =
        std::async(std::launch::async,
            [](T&& vec) mutable
            {
                return vec[0];
            },
            std::forward<T>(obj));

    return fut.get();
}

int main(int argc, char const *argv[])
{
    std::vector<std::string> lvalue{"Testing"};

    // calling with what I assume is an lvalue reference does NOT compile
    std::cout << accessValueAsync(lvalue) << std::endl;

    // calling with rvalue reference compiles
    std::cout << accessValueAsync(std::move(lvalue)) << std::endl;

    // I want both to compile.

    return 0;
}

对于非编译情况,这里是可以理解的错误消息的最后一行:

main.cpp|13 col 29| note:   no known conversion for argument 1 from ‘std::vector<std::basic_string<char> >’ to ‘std::vector<std::basic_string<char> >&’

我感觉这可能与 T&& 的推导方式有关,但我无法确定确切的故障点并修复它。有什么建议吗?

谢谢!

编辑:我正在使用 gcc 4.7.0 以防万一这可能是编译器问题(可能不是)

最佳答案

根据我的理解,您不能通过 async 使用函数期望非常量左值引用作为参数,因为 async将始终在内部制作它们的拷贝(或将它们移动到内部)以确保它们存在并且在创建的线程的整个运行时间内有效。

具体来说,标准说的是 async(launch policy, F&& f, Args&&... args) :

(§30.6.8)

(2) Requires: F and each Ti in Args shall satisfy the MoveConstructible requirements. INVOKE(DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2, 30.3.1.2) shall be a valid expression.

(3) Effects: [...] if policy & launch::async is non-zero — calls INVOKE(DECAY_COPY (std::forward<F>(f)),DECAY_COPY (std::forward<Args>(args))...) (20.8.2, 30.3.1.2) as if in a new thread of execution represented by a thread object with the calls to DECAY_COPY() being evaluated in the thread that called async. Any return value is stored as the result in the shared state. Any exception propagated from the execution of INVOKE (DECAY_COPY (std::forward(f)), DECAY_COPY (std::forward(args))...) is stored as the exceptional result in the shared state.
The thread object is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.

不幸的是,这意味着您甚至无法将引用替换为 std::reference_wrapper ,因为后者不可移动构造。我想使用 std::unique_ptr而不是引用会起作用(但是,这意味着您的函数参数将始终存在于堆中)。

(编辑/更正)
当我意识到 std::reference_wrapper 时,我正在研究相关问题实际上启用了一个解决方法,尽管我在上面声称相反。

如果你定义了一个将左值引用包装在 std::reference_wrapper 中的函数, 但保留右值引用不变,您可以传递 T&&在将其移交给 std::async 之前通过此函数进行论证.我调用了这个特殊的包装函数 wrap_lval下面:

#include <thread>
#include <future>
#include <utility>
#include <iostream>
#include <vector>
#include <type_traits>

/* First the two definitions of wrap_lval (one for rvalue references,
   the other for lvalue references). */

template <typename T>
constexpr T&&
wrap_lval(typename std::remove_reference<T>::type &&obj) noexcept
{ return static_cast<T&&>(obj); }

template <typename T>
constexpr std::reference_wrapper<typename std::remove_reference<T>::type>
wrap_lval(typename std::remove_reference<T>::type &obj) noexcept
{ return std::ref(obj); }


/* The following is your code, except for one change. */
template <typename T>
std::string accessValueAsync(T&& obj)
{

  std::future<std::string> fut =
    std::async(std::launch::async,
           [](T&& vec) mutable
           {
             return vec[0];
           },
           wrap_lval<T>(std::forward<T>(obj)));   // <== Passing obj through wrap_lval

  return fut.get();
}

int main(int argc, char const *argv[])
{
  std::vector<std::string> lvalue{"Testing"};

  std::cout << accessValueAsync(lvalue) << std::endl;

  std::cout << accessValueAsync(std::move(lvalue)) << std::endl;

  return 0;
}

通过此更改,两次调用 accessValueAsync编译和工作。第一个使用左值引用,自动将其包装在 std::reference_wrapper 中。 .后者在 std::async 时自动转换回左值引用调用 lambda 函数。

关于c++ - 完美转发到异步 lambda,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13813838/

相关文章:

c++ - map<A, set<A*>> 与 set<A> 其中 A 包含 A* 的集合

c++ - 多线程时一直调用对象析构函数,但对象没有超出范围

java - 将参数传递给谓词

c++ - 智能指针如何在 delete 和 delete[] 之间进行选择?

c++ - 我如何解决 "[Error] invalid use of incomplete type ' 类 SLLNode'"链表

c++ - OOP可以在嵌入式C中使用吗?

c++ - StepTimer.GetTotalSeconds() 产生的值并不总是增加

c++ - 仅当语句匹配时如何将参数发送给函数?

Java 8 lambda : convert Collection to Map of element, 迭代位置

c# - 在这种情况下,为什么在 lambda 中调用方法时将方法组传递给重载方法会导致歧义?