【问题标题】:How is it possible to pass move-only types (e.g. std::unique_ptr) by value?如何按值传递仅移动类型(例如 std::unique_ptr)?
【发布时间】:2018-12-20 01:47:33
【问题描述】:

如果我们以std::unique_ptr 为例,将只移动类型传递给接收器函数(例如,获取指针所有权的构造函数)的普遍智慧是按值传递它并在调用时移动它地点。例如

class Sink {
    public:
        Sink(std::unique_ptr<Foo> foo) : foo(std::move(foo)) {}

    private:
        std::unique_ptr<Foo> foo;
};

这怎么可能?编译器不应该能够立即推断出std::unique_ptr 是一个只移动类型并拒绝使用Sink(std::unique_ptr&lt;Foo&gt;) 吗?这里的按值传递规则有什么特别之处吗(即按值传递的东西被复制到该函数激活框架中)?

提前致谢。

【问题讨论】:

  • foo 不用复制进去,也可以移动进去
  • 那很好,但不幸的是(或作为历史的一个怪癖)“可移动”概念是相当新的,必须被固定在一种已经非常成熟的语言上。
  • @vu1p3n0x 这是这里缺少的见解,我一直认为按值传递会调用复制构造函数,尽管我现在知道这将取决于在调用站点传入的值的类型(不带移动构造函数的左值和右值的复制构造函数,带移动构造函数的右值的移动构造函数)。

标签: c++ smart-pointers move-semantics pass-by-value


【解决方案1】:

当然可以,只要您将右值传递给 Sink 的构造函数或移动参数即可。在这两种情况下,都会为参数调用移动构造函数,而不是(删除的)复制构造函数。

注:我不认为std::move 在这里是一个好主意,因为这样你的函数中就会有一个移动的对象。静态分析可能会发现这一点,但它是错误的来源。

#include <memory>

struct Foo {};

class Sink {
    public:
        Sink(std::unique_ptr<Foo> foo) : foo(std::move(foo)) {}

    private:
        std::unique_ptr<Foo> foo;
};

int main() {
    // rvalue
    Sink s(std::make_unique<Foo>());

    // std::move (can be dangerous)
    auto f = std::make_unique<Foo>();
    Sink ss(std::move(f));

    // By value is not allowed
    auto ff = std::make_unique<Foo>();
  //Sink sss(ff); // BOOM!
}

Live on Wandbox

【讨论】:

  • “移动的对象是未定义的行为”不,这是错误的。有多种方法可以使用调用 UB 的移动对象,但非移动对象也是如此。
  • 那么更准确的说法是传值并不总是调用复制构造函数,而是根据传入的值是否是左值来调用复制或移动构造函数还是右值引用?
  • @AUD_FOR_IUV 如您所见,这取决于值的种类。右值调用移动构造函数(如果有,则复制),左值调用复制构造函数。
  • “移出对象的状态是不确定的”这也是错误的。存在具有不确定状态的已移动对象,但也有具有不确定状态的未移动对象,以及状态已完全确定的已移动对象(std 内部和外部)。 unique_ptr&lt;T&gt; 是对象的移出状态完全确定的类型之一。
  • @Yakk 好吧,我绝对不是语言律师,足以争论这个问题,所以我只是删除了这个子句。我希望您同意,在其周围移动对象是潜在的错误来源。
猜你喜欢
  • 2012-07-01
  • 1970-01-01
  • 2012-04-14
  • 2019-04-01
  • 2015-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多