通过引用将参数传递给 std::async 失败

2021-12-14 00:00:00 multithreading asynchronous c++ c++11

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

I've noticed that it's impossible to pass a non-const reference as an argument to std::async.

#include <functional>
#include <future>

void foo(int& value) {}

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

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

My compiler (GCC 4.8.1) gives the following error for this example:

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

但是如果我将传递给 std::async 的值包装在 std::reference_wrapper 中,则一切正常.我认为这是因为 std::async 按值获取它的参数,但我仍然不明白错误的原因.

But if I wrap the value passed to std::async in std::reference_wrapper, everything is OK. I assume this is because std::async takes it's arguments by value, but I still don't understand the reason for the error.

推荐答案

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

It's a deliberate design choice/trade-off.

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

First, it's not necessarily possible to find out whether the functionoid passed to async takes its arguments by reference or not. (If it's not a simple function but a function object, it could have an overloaded function call operator, for example.) So async cannot say, "Hey, let me just check what the target function wants, and I'll do the right thing."

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

So the design question is, does it take all arguments by reference if possible (i.e. if they're lvalues), or does it always make copies? Making copies is the safe choice here: a copy cannot become dangling, and a copy cannot exhibit race conditions (unless it's really weird). So that's the choice that was made: all arguments are copied by default.

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

But then, the mechanism is written so that it actually fails to then pass the arguments to a non-const lvalue reference parameter. That's another choice for safety: otherwise, the function that you would expect to modify your original lvalue instead modifies the copy, leading to bugs that are very hard to track down.

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

But what if you really, really want the non-const lvalue reference parameter? What if you promise to watch out for dangling references and race conditions? That's what std::ref is for. It's an explicit opt-in to the dangerous reference semantics. It's your way of saying, "I know what I'm doing here."

相关文章