c++ 通过右值引用将std::unique_ptr传递给构造函数

jogvjijk  于 2023-03-25  发布在  其他
关注(0)|答案(2)|浏览(198)

考虑下面的两个构造函数:

// 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值方法和按值方法来显示差异,但是失败了。在所有情况下,两个构造函数都一样工作,数据总是被移动。答案的作者似乎表明,根据内部代码路径,移动可以被忽略。进一步引用答案:
问题是它没有。不能保证它已经被移动了。它可能已经被移动了,但你只能通过查看源代码来知道。你不能仅仅从函数签名中分辨出来。
有没有人能给我举一个例子,说明这种情况下,移动可以被忽略?

u5i3ibmn

u5i3ibmn1#

例如,在下面的代码中,两个类都没有使用传入的指针:

#include <memory>
#include <iostream>

class A
{
public:

    A(std::unique_ptr<int>&& data_) {}
};

// Passing data_ by value:
class B
{
public:

    B(std::unique_ptr<int> data_) {}
};

int main()
{
    auto a = std::make_unique<int>(5);
    A{std::move(a)};
    auto b = std::make_unique<int>(5);
    B{std::move(b)};
    std::cout << (a == nullptr) << "\n";
    std::cout << (b == nullptr) << "\n";
}

https://godbolt.org/z/dbbcKbs79
A中,指针通过r值引用传递到data_,引用没有被使用,所以原始的a指针没有被修改。
B中,指针被移动到临时值data_中,指针不被使用,但原始的b指针仍然被重置为null。

llew8vvj

llew8vvj2#

你在这里展示的还不够。通常,我们在转移所有权 * 时传递value *:

class B
{
    std::unique_ptr<int> data;

public:
    B(std::unique_ptr<int> data)
      : data{std::move(data)}
    {}
};

不要在不需要转移的地方使用std::unique_ptr(使用原始指针,或者如果保证非空的话,也可以使用引用)。如果所有权 * 可能会或可能不会转移 *,取决于编译时未知的东西,那么这是唯一(并且非常罕见)用于通过右值引用传递的方法。在我的整个职业生涯中,我从来没有需要这样做过。
按值传递的一个具体好处是,如果构造函数在其data被赋值之前抛出,则参数中的临时副本将被析构,并且我们最终不会从调用方移动失败。
但主要的好处是清晰-而不是读者问我们为什么要引用调用者的指针,我们只是做了明显和预期的事情。
当通过值传递时,目标对象肯定不再属于调用者。当通过引用传递时,我们不知道新的A是否已经拥有所有权,除非我们检查A的实现。
简单地说,**B()明确地是一个 * 汇 *,而A()可能是,也可能不是。

相关问题