考虑下面的两个构造函数:
// Passing data_ by r-value reference:
class A
{
public:
A(std::unique_ptr<int>&& data_);
// ...
};
// Passing data_ by value:
class B
{
public:
B(std::unique_ptr<int> data_);
// ...
};
我一直在问自己哪个构造函数更适合 * 获取data_
的所有权 *。这个nice answer似乎表明通过r值引用的方法可能不是最好的方法。从这个答案的建议部分:
**(D)通过r值引用:**如果一个函数可能声明所有权,也可能不声明所有权(取决于内部代码路径),那么就用&&
。但我强烈建议尽可能不要这样做。
我试着写一个单元测试,用r值方法和按值方法来显示差异,但是失败了。在所有情况下,两个构造函数都一样工作,数据总是被移动。答案的作者似乎表明,根据内部代码路径,移动可以被忽略。进一步引用答案:
问题是它没有。不能保证它已经被移动了。它可能已经被移动了,但你只能通过查看源代码来知道。你不能仅仅从函数签名中分辨出来。
有没有人能给我举一个例子,说明这种情况下,移动可以被忽略?
2条答案
按热度按时间u5i3ibmn1#
例如,在下面的代码中,两个类都没有使用传入的指针:
https://godbolt.org/z/dbbcKbs79
在
A
中,指针通过r值引用传递到data_
,引用没有被使用,所以原始的a
指针没有被修改。在
B
中,指针被移动到临时值data_
中,指针不被使用,但原始的b
指针仍然被重置为null。llew8vvj2#
你在这里展示的还不够。通常,我们在转移所有权 * 时传递value *:
不要在不需要转移的地方使用
std::unique_ptr
(使用原始指针,或者如果保证非空的话,也可以使用引用)。如果所有权 * 可能会或可能不会转移 *,取决于编译时未知的东西,那么这是唯一(并且非常罕见)用于通过右值引用传递的方法。在我的整个职业生涯中,我从来没有需要这样做过。按值传递的一个具体好处是,如果构造函数在其
data
被赋值之前抛出,则参数中的临时副本将被析构,并且我们最终不会从调用方移动失败。但主要的好处是清晰-而不是读者问我们为什么要引用调用者的指针,我们只是做了明显和预期的事情。
当通过值传递时,目标对象肯定不再属于调用者。当通过引用传递时,我们不知道新的
A
是否已经拥有所有权,除非我们检查A
的实现。简单地说,**
B()
明确地是一个 * 汇 *,而A()
可能是,也可能不是。