【问题标题】:unique_ptr, move constructor, and why always attempt to access private memberunique_ptr,移动构造函数,以及为什么总是尝试访问私有成员
【发布时间】:2013-02-01 15:28:57
【问题描述】:

我经常遇到这个问题,我相信移动构造函数是有序的,但我认为复制构造函数是问题所在,隐藏它似乎不起作用。

代码:

template <class T>
class LinkedList{
public:
    //
    LinkedList() {}
    LinkedList(const T &data);
    LinkedList(const T &data, const LinkedList &node);
    LinkedList(const LinkedList &object);
    LinkedList &operator=(const LinkedList &object);

    ~LinkedList() {}

    std::shared_ptr<LinkedList> push_back(const T& data);

private:
    T data;
    std::unique_ptr<LinkedList> link;

    std::unique_ptr<LinkedList> LinkFactory(const LinkedList &node);

    std::shared_ptr<LinkedList> CreateStartNode(const T &data);
    std::shared_ptr<LinkedList> CreateNode(const T &data, const LinkedList &node);
};

发生错误的特定行是:

LinkedList<T>::LinkedList(const LinkedList<T> &object) : data(object.data),  
link(std::move(object.link)) {}

我试图移动而不是复制复制构造函数中的链接,但无济于事。如果移动构造函数是设计而不是合成的,那会更好吗?

【问题讨论】:

  • 由于有复制构造函数,编译器不会合成移动构造函数
  • 如果使用复制构造函数定义移动构造函数,“有时”将调用移动构造函数。那么最好简单地定义和隐藏复制构造函数的实现吗? @dribeas 如果我没有定义复制构造函数,那么会为我合成一个并成为问题,对吗?
  • 正确,您需要您的复制构造函数。我只是说拥有一个复制构造函数会抑制移动构造函数的隐式声明,因此您需要声明和定义它(即使定义是普通的= default,在您的情况下应该可以工作)。这解决了您的错误消息,但留下了复制构造函数是否应该窃取源代码内容的问题,嗯......不是那么多:它不应该。
  • 很遗憾,我使用的是 VS10,所以不支持 = delete= default

标签: c++ c++11 copy-constructor unique-ptr move-constructor


【解决方案1】:

你不能移动一个常量对象,因为object被声明为常量,object.link也是常量。

这看起来像是一个损坏的设计,因为通常该构造函数是一个 copy 构造函数,但是您尝试将链接 move 出参数,这意味着您尝试窃取它拥有的资源。你有一个 LinkFactory 方法,看起来你应该使用它,如果它符合名称的承诺。

【讨论】:

    【解决方案2】:

    采用lvalue 引用的构造函数是复制构造函数而不是移动构造函数。如果引用是const,则无法修改现有对象,因此无法从中移动。 (你不应该删除const,因为这会给你一个奇怪的破坏性复制语义,并且移动语义被添加到语言中以避免这种奇怪)。

    由于unique_ptr 成员,您的类不可复制,因此您根本不应该提供复制构造函数。您可以提供一个移动构造函数:

    LinkedList<T>::LinkedList(LinkedList<T> && object) : 
        data(object.data), link(std::move(object.link)) {}
    

    但没有必要,因为隐式生成的移动构造函数会这样做(唯一的区别是数据被移动而不是复制)。

    请记住,通常不能移动命名变量,除非您明确这样做:

    LinkedList<int> l1;
    LinkedList<int> l2(l1);             // ERROR: tries to copy
    LinkedList<int> l3(std::move(l1));  // OK: explicit move
    

    【讨论】:

    • 谢谢大家。但是如果我没有定义一个复制构造函数,那么就会为我合成一个。定义了移动构造函数后,调用它的频率可能比调用副本的频率高。这实际上如何帮助一劳永逸地解决所有这些问题?
    • @user633658:由于您的类包含不可复制的成员,因此没有合成复制构造函数;它被隐式声明为已删除。没有明智的方法可以复制您的课程,因此您应该将其删除。它可移动的,并且隐式移动构造函数做了正确的事情,所以也不需要提供它。只是不要试图复制类的实例,因为那是行不通的。
    • 是否隐含声明已删除LinkedList(const LinkedList&amp; object) = delete
    • 我刚刚删除了构造函数,确实合成了一个。 This diagnostic occurred in the compiler generated function 'LinkedList&lt;T&gt;::LinkedList(const LinkedList&lt;T&gt; &amp;)' 我将在类中放置一个移动构造函数,并且应该调用它来代替副本。我还删除了运算符分配。
    • @user633658:正如我所说,您不需要提供移动构造函数,因为它是为您合成的;但它只会在使用 rvalue 调用时使用(例如临时对象,或从函数返回的对象)。要移动命名变量,您需要使用 std::move 将其显式转换为合适的 rvalue
    【解决方案3】:

    您想要实现的语义是什么?在你的副本中 构造函数,被复制的对象是const(即 通常是正确的);试图移动其中的任何东西都需要它 成为非常量。这看起来像一个设计缺陷,但如果不是, 建立链接mutable 可能是答案。

    【讨论】:

    • 我只是想实现一种复制对象的方法,但认识到 unique_ptr 不支持复制/分配并尝试使用 'std::move' 来代替。然而,正如所指出的, const 定义虽然在语义上是正确的,但会导致双重问题。如果我摆脱副本并切换到移动,那么我可能不得不处理导致更多问题的合成。
    • @user633658 我可以看到您在该级别上尝试做什么。但在更高的层次上。复制对象的结果应该是什么。它是否应该更改被复制的对象。 (IIUC,您的版本将创建一个副本,它会劫持列表中的所有以下对象;即,它会更改被复制的对象。)
    【解决方案4】:

    我如下定义了我的复制构造函数,并且我没有使用 unique_ptr 的编译时或链接错误:

    LinkedList<T>::LinkedList(const LinkedList &other){
    data = other.data;
    link(std::move(other.link.get()));
    }
    

    感谢大家回答这个问题。

    【讨论】:

      猜你喜欢
      • 2013-03-27
      • 2015-05-25
      • 2014-01-05
      • 2020-07-09
      • 2017-01-10
      • 2017-08-16
      • 2019-10-16
      相关资源
      最近更新 更多