31

我注意到不可能将非常量引用作为参数传递给std::async.

#include <functional>
#include <future>

void foo(int& value) {}

int main() {
    int value = 23;
    std::async(foo, value);
}

我的编译器(GCC 4.8.1)在这个例子中给出了以下错误:

error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’

但是,如果我将传递给std::asyncin的值包装起来std::reference_wrapper,一切正常。我认为这是因为std::async它的参数是按价值计算的,但我仍然不明白错误的原因。

4

3 回答 3

47

这是一个深思熟虑的设计选择/权衡。

首先,不一定可以找出传递给的 functionoid 是否async通过引用获取其参数。(例如,如果它不是一个简单的函数而是一个函数对象,它可能有一个重载的函数调用运算符。)所以async不能说,“嘿,让我检查一下目标函数想要什么,我会做正确的事情。”

所以设计问题是,如果可能的话,它是否通过引用来获取所有参数(即,如果它们是左值),还是总是复制?在这里制作副本是安全的选择:副本不能变得悬空,并且副本不能表现出竞争条件(除非它真的很奇怪)。这就是做出的选择:默认情况下会复制所有参数。

但是,该机制被编写为实际上无法将参数传递给非常量左值引用参数。这是另一个安全选择:否则,您希望修改原始左值的函数会修改副本,从而导致难以追踪的错误。

但是,如果您真的非常想要非常量左值引用参数怎么办?如果您承诺要注意悬空引用和竞争条件怎么办?这std::ref就是为了。这是对危险引用语义的明确选择。这是你的表达方式,“我知道我在这里做什么。”

于 2013-08-21T14:48:36.690 回答
25

std::async(以及其他执行完美转发的函数)查看您传递的参数的类型以确定要做什么。他们没有考虑最终将如何使用该论点。因此,要通过引用传递对象,您需要告知std::async您正在使用引用。但是,简单地传递一个引用不会这样做。您必须使用通过引用std::ref(value)传递。value

于 2013-08-21T14:49:45.720 回答
15

问题本身仅与以下几点相关std::async():在定义操作结果时,std::async()使用std::result_of<...>::type其所有参数被std::decay<...>::type'ed。这是合理的,因为std::async()采用任意类型并将它们转发以将它们存储在某个位置。为了存储它们,函数对象和参数都需要值。因此,std::result_of<...>类似这样使用:

typedef std::result_of<void (*(int))(int&)>::type result_type;

...并且由于int不能绑定到int&(不是需要绑定int左值类型int&),因此失败。在这种情况下失败意味着std::result_of<...>没有定义嵌套的type.

一个后续问题可能是:这种用于实例化的类型是什么std::result_of<...>?这个想法是滥用由 组成的函数调用语法ResultType(ArgumentTypes...):而不是结果类型,而是传递函数类型,并std::result_of<...>确定在调用具有给定参数列表的函数类型时调用的函数的类型。对于函数指针类型,它并不是那么有趣,但函数类型也可以是需要考虑重载的函数对象。所以基本上,std::result_of<...>是这样使用的:

typedef void (*function_type)(int&);
typedef std::result_of<function_type(int)>::type result_type; // fails
typedef std::result_of<function_type(std::reference_wrapper<int>)>::type result_type; //OK
于 2013-08-21T14:54:22.557 回答