为什么我不能在std::future参数中使用引用?

7
为什么以下代码(在Ideone上)会给我一个错误?
#include <future>
#include <iostream>
#include <string>

int main()
{
    int foo = 0;
    bool bar = false;
    std::future<std::string> async_request = std::async(
        std::launch::async,
        [=, &foo](bool& is_pumping_request) -> std::string {
            return "str";
        },
        bar
    );
    std::cout << async_request.get() << std::endl;
}

输出:
In file included from /usr/include/c++/5/future:38:0,
                 from prog.cpp:1:
/usr/include/c++/5/functional: In instantiation of 'struct std::_Bind_simple<main()::<lambda(bool&)>(bool)>':
/usr/include/c++/5/future:1709:67:   required from 'std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = main()::<lambda(bool&)>; _Args = {bool&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = std::basic_string<char>]'
prog.cpp:15:2:   required from here
/usr/include/c++/5/functional:1505:61: error: no type named 'type' in 'class std::result_of<main()::<lambda(bool&)>(bool)>'
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/5/functional:1526:9: error: no type named 'type' in 'class std::result_of<main()::<lambda(bool&)>(bool)>'
         _M_invoke(_Index_tuple<_Indices...>)
         ^

然而,如果我在参数列表中将bool&更改为bool,它会成功编译。为什么呢?
2个回答

13

std::thread类似,std::async将参数按值传递给“函数”。如果您有一个需要引用的函数,则需要使用std::ref来包装传递给 async 的变量。

#include <future>
#include <iostream>
#include <string>

int main()
{
    int foo = 0;
    bool bar = false;
    std::future<std::string> async_request = std::async(
        std::launch::async,
        [=, &foo](bool& is_pumping_request) -> std::string {
            return "str";
        },
        std::ref(bar)
    );
    std::cout << async_request.get() << std::endl;
}

在线例子

如果函数使用了 const &,那么你需要使用 std::cref


cref 是一个好的编程实践,但是 ref 甚至不传递任何参数(传递一个副本)仍然可以工作(根据“工作”的定义)。 - T.C.

8
考虑一下如果它通过引用绑定bar会发生什么。
那么每次调用std::async时,您传递的每个值都必须持续到异步完成为止。
这将是意外内存损坏的原因。因此,默认情况下,std::async会复制您传递给它的所有内容。
然后它在输入的副本上运行任务。
聪明的是,它告诉您调用的代码该值是非永久性的,并将其移入代码中。而左值引用无法绑定到移动的值。
您可以使用std::reference_wrapper覆盖此行为。async理解reference_wrapper,并自动存储对这些值的引用并将它们按引用传递给被调用的代码。
创建reference_wrapper的简单方法是调用std::ref。
int foo = 0;
bool bar = false;
std::future<std::string> async_request = std::async(
    std::launch::async,
    [=, &foo](bool& is_pumping_request) -> std::string {
        return "str";
    },
    std::ref(bar)
);
std::cout << async_request.get() << std::endl;

并且它只需要运行。

这种“仅显式传递引用”的特性是绑定操作的安全保障;因为绑定执行可以持久存在于当前状态之外,所以调用者只需显式地按引用进行绑定,从而降低意外悬挂引用的风险。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接