简体   繁体   English

为什么在传递给异步时从默认构造函数调用移动构造函数?

[英]Why is the move constructor called from the default one when it is passed to async?

i have a class like this:我有一个像这样的 class:

class move_only
{
public:
    int valut;
    move_only() {}
    move_only(move_only&&);
    void operator()();
};

move_only::move_only(move_only&&)
{
    cout << "here";
}

void move_only::operator()()
{
}

i wonder why when you call:我想知道为什么当你打电话时:

auto flow = std::async(move_only());

first called首先调用

move_only() 

(which is understandable), but why then: (这是可以理解的),但是为什么:

move_only(move_only&&);

and only then只有这样

void move_only::operator()

I will be glad to help, thank我很乐意提供帮助,谢谢

The backtrace of gcc 10.2 at the moment the move constructor is invoked:调用移动构造函数时 gcc 10.2 的回溯:

1 move_only::move_only 1 move_only::move_only
2 std::_Head_base<0ul, move_only, false>::_Head_base<move_only> 2 std::_Head_base<0ul, move_only, false>::_Head_base<move_only>
3 std::_Tuple_impl<0ul, move_only>::_Tuple_impl<move_only> 3 std::_Tuple_impl<0ul, move_only>::_Tuple_impl<move_only>
4 std::tuple<move_only>::tuple<move_only, true, true> 4 std::tuple<move_only>::tuple<move_only, true, true>
5 std::thread::__make_invoker<move_only> 5 std::thread::__make_invoker<move_only>
6 std::async<move_only> 6 std::async<move_only>
7 std::async<move_only> 7 std::async<move_only>
8 main 8个主要

Please note how move_only , the name of your class, is smoothly transferred 6 levels of indirection to the point where it is really needed!请注意move_only ,您的 class 的名称,如何平滑地转移 6 级间接到真正需要的点!

Let's work this backtrace out, in reverse order:让我们以相反的顺序处理这个回溯:

  1. main calls async - that's what we can see. main调用async - 这就是我们可以看到的。 The rest the implementation rest的实现
  2. async calls itself with a proper execution policy explicitly given as an argument async使用作为参数显式给出的适当执行策略调用自身
  3. async is a friend to future and calls __future_base::_S_make_async_state to create a __state , which is a shared pointer that will be used by async to create a future . asyncfuture的朋友,它调用__future_base::_S_make_async_state来创建一个__state ,这是一个共享指针, async将使用它来创建一个future We don't see _S_make_async_state - it is a trivial function and perhaps gcc has inlined it even in Debug miode, this is a bit weird.我们看不到_S_make_async_state - 它是一个微不足道的 function ,也许 gcc 甚至在调试 miode 中也内联了它,这有点奇怪。
  4. The first argument of _S_make_async_state is of type std::tuple . _S_make_async_state的第一个参数是std::tuple类型。 It is generated by std::thread::__make_invoker它由std::thread::__make_invoker
  5. std::tuple inherits publicly from _Tuple_impl std::tuple公开继承自_Tuple_impl
  6. _Tuple_impl inherits privately from _Head_base _Tuple_impl私有继承自_Head_base
  7. _Head_base has a data member _M_head_impl , of type _Head : _Head_base有一个数据成员_M_head_impl ,类型为_Head
   _Head _M_head_impl;

Please notice that apparently this member is embedded "by value" - we can see no evidence of pointer, * , reference, ' & , etc. But this is C++, these might be hidden in the definition of _Head .请注意,这个成员显然是“按值”嵌入的——我们看不到指针、 * 、引用、' &等的证据。但这是 C++,这些可能隐藏在_Head的定义中。 But when we look at the backtrace, we can identify _Head with your class name, move_only .但是当我们查看回溯时,我们可以用您的 class 名称move_only _Head Hurray!欢呼!

This data member is constructed in this line:此数据成员在此行中构造:

_M_head_impl(std::forward<_UHead>(__h))

Here, std::forward provides the perfect forwarding of your function or function object: if you use an r-value as the argument, the constructor of _Head_base will have this information passed all the way from your main .在这里, std::forward提供了您的 function 或 function object 的完美转发:如果您使用 r-value 作为参数,则此信息将从您的main值构造函数_Head_base传递。 No references, no pointers - the member will be constructed by value.没有引用,没有指针 - 成员将按值构造。 But since it is being constructed from a temporary object and the move constructor is available, the move constructor will be used here.但由于它是从临时 object 构造的,并且移动构造函数可用,因此此处将使用移动构造函数。 The mystery is solved.谜团解开了。

The really big question is this: is this call to move constructor guaranteed by the standard or is it an implementation "detail"?真正的大问题是:这个移动构造函数的调用是由标准保证的还是实现“细节”? Since C++ sports a feature like guaranteed or just optional copy elision, one should write the constructors in such a way that they have no side effects and it should not matter whether an object is copied, moved or "copy-elised".由于 C++ 具有保证或只是可选复制省略之类的功能,因此应该以没有副作用的方式编写构造函数,并且 object 是否被复制、移动或“复制删除”都无关紧要。

The next question: how can I guess, given the signature of std::async , that the function object will be copied, moved or passed by reference?下一个问题:鉴于std::async的签名,我怎么能猜到 function object 将被复制、移动或通过引用传递? Let's see:让我们来看看:

std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
    async( Function&& f, Args&&... args );

This may be confusing at first, for clearly, without doubt, std::async accepts all its parameters by reference and no move constructor can be called here .起初这可能会令人困惑,因为毫无疑问, std::async通过引用接受其所有参数,并且不能在此处调用移动构造函数。 But can it be called elsewhere?但它可以在其他地方调用吗?

One hint is perfect forwarding seen in && applied to arguments with a type deduced from a template parameter.一个提示是在&&中看到的完美转发应用于 arguments,其类型从模板参数推导出来。 In particular, f is being passed by perfect forwarding.特别是, f是通过完美转发传递的。 If anyone uses perfect forwarding, they want to keep the information whether the argument is an l- or r-value, so be prepared for invocation of a copy or move constructor somewhere .如果有人使用完美转发,他们希望保留参数是左值还是右值的信息,因此请准备好在某处调用复制或移动构造函数。 The second hint is purely logical.第二个提示纯粹是合乎逻辑的。 std::async is supposed to be rock-solid, safe means of using multithreading. std::async应该是使用多线程的坚如磐石、安全的方法。 If it is to be safe, it must do literally everything to obtain all its arguments by value.如果要安全,它必须尽一切努力按价值获取其所有 arguments。 If they were passed by reference or pointer, the parameters passed to a function called asynchronously may cease to exist long before this function returns.如果它们是通过引用或指针传递的,则传递给异步调用的 function 的参数可能在此 function 返回之前很久就不再存在。 In this case the function parameter would at some point of time turn into rubbish and the function would have no clue as to when.在这种情况下,function 参数将在某个时间点变成垃圾,而 function 将不知道何时。 So, you should expect that by default all arguments to std::async are effectively (and perhaps indirectly ) passed by value, and keep your fingers crossed that the implementation is so smart that it can actually move them whenever it is legitimate to do so.因此,您应该期望默认情况下,所有 arguments 到std::async都有效地(并且可能是间接地)按值传递,并保持手指交叉,以便实现非常聪明,以至于它可以在合法时实际移动它们这样做.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM