1

仅考虑可移动不可复制的对象(例如,std::threadstd::unique_ptr),我想通过将其作为参数传递给构造函数来转移此类对象包含的资源的所有权。我正在比较两种方法:构造函数按值获取对象与按右值引用获取对象。

作为 的示例std::thread,请考虑以下类Value,其构造函数采用std::thread by value

#include <thread>
#include <utility>

struct Value {
   Value(std::thread th): th_(std::move(th)) {}
   std::thread th_;
};

所有权从参数对象转移到参数对象,th最后转移到数据成员对象,th_

考虑一个类似的类 ,Reference它的构造函数在这种情况下std::thread采用右值引用

struct Reference {
   Reference(std::thread&& th): th_(std::move(th)) {}
   std::thread th_;
};

在这种情况下,所有权从参数直接转移到数据成员对象th_.

据我了解,在按值传递的情况下,参数对象和数据成员对象都是移动构造的,而对于按引用传递的情况,只有数据成员是移动构造的。总而言之,后一种方法似乎更好,因为它只需要一次移动操作,因此效率更高。

但是,是否有任何理由更喜欢按值传递的方法而不是按引用传递的方法?

4

1 回答 1

7

按值传递是自我记录,而按引用传递则不是。读者可以立即清楚地看到:

  1. 构造函数将无条件地获得由只移动类型表示的资源的所有权;
  2. 如果移动构造由于某种原因引发异常而失败,则调用者有责任处理它;(*)
  3. 如果构造函数通过抛出异常退出,资源将被处理掉。

(同样地,在实现中很难或不可能出现导致上述假设不成立的错误。)

当然,最终这些好处是否超过额外移动建设的成本的问题是基于意见的问题。

(*) 当然,这不适用于构造函数内部执行的移动,从参数到成员的移动。然而,这里的重点是调用者可以控制他们的对象发生的事情:要么将其移动到构造函数参数中,要么在该步骤发生任何故障的情况下,调用者可以进行损坏控制(可能保留价值)。

于 2019-11-21T23:17:06.653 回答