【问题标题】:Unable to move temporary object into the *this object无法将临时对象移动到 *this 对象中
【发布时间】:2017-08-14 20:01:59
【问题描述】:

在尝试使用 C++11 创建二叉搜索树 (BST) 时遇到了障碍。 我无法从创建它的函数中提取正确创建的 BST。 该函数从文件中读取数据(在这种情况下,每行只有一个数字)并从中构建 BST。循环中的构建部分正常工作。 问题是我无法将临时对象移动到我想要的位置。

放大我认为的问题,请参阅编辑 3。

更多上下文: BST<T> 是一个公开派生自 std::unique_ptr<BSTknoop<T>> 的类
BST<T> 还继承了 unique_ptr<BSTknoop<T>> 的构造函数与

template<class T>
using BSTknoopptr=std::unique_ptr<BSTknoop<T>>; // alias template

using BSTknoopptr<T>::BSTknoopptr; // in the body of the BST<T> class declaration`

BSTknoop&lt;T&gt; 是一个包含 3 个字段的数据结构 一个 T 对象来保存节点数据 左右两个BST&lt;T&gt;对象(每个孩子都是一棵树)

目标是将我新创建的 BST 移动到调用对象中。这个想法是,在 main 中,您可以调用 BST&lt;int&gt; tree; tree.lees(ifs);ifs 是一个打开的输入文件流),然后 tree 保存正确填充的 BST。

功能:

template<class T>
istream& BST<T>::lees(istream& is){
    string lijn;
    T knoopwaarde;
    getline(is,lijn);
    knoopwaarde = stoi(lijn);
    BST<T> temptree(new BSTknoop<T>());
    temptree->sl = knoopwaarde;

    for(int i=1; i<DATA_SET_LENGTH ; i++){
        getline(is,lijn);
        knoopwaarde=stoi(lijn);
        temptree.add(knoopwaarde);
        cout<<temptree;
    }
    this->swap(temptree);   /* This does not work */ 
    /* *this = move(temptree);   this does not work either*/
    return is;
}

我还尝试从函数中返回 BST&lt;T&gt; 并将该结果移动到主函数中。那也没用。

程序在运行时因未知信号而崩溃。

旁注:我不确定为什么BST&lt;T&gt; temptree(new BSTknoop&lt;T&gt;()); 在模板方面起作用。构造工作是因为BST&lt;T&gt; 继承了unique_ptr&lt;BSTknoop&lt;T&gt;&gt; 的构造函数

编辑 1:BST&lt;T&gt; 类的声明:

template <class T>
class BST:public BSTknoopptr<T>{
    using BSTknoopptr<T>::BSTknoopptr;
public:
    friend istream& operator>>(istream& is, BST<T>& bb){
        return bb.lees(is);
    }
    friend ostream& operator<<(ostream& os, const BST<T>& bb){
        //return bb.schrijflevelorder(os);
        return bb.schrijfKnoop(os);
    }
    void add(const T&);
    ostream& schrijf(ostream&);
    ostream& schrijfKnoop(ostream&) const;
    int aantalSleutels() const;
    istream& lees(istream&);   
    ostream& schrijflevelorder(ostream& os) const;
private:
};

BSTknoop&lt;T&gt; 类的声明:

template <class T>
class BSTknoop{
    friend class BST<T>;
public:
    BSTknoop() {}
    explicit BSTknoop(T _sl) : sl(_sl) {}
private:
    T sl;
    BST<T> links,rechts;
};

编辑 2:我编写了一个移动构造函数和移动赋值运算符。我已确保使用BST&lt;T&gt;()=default; 保留默认构造函数,但我很困惑:BST 类没有任何成员,我必须为其实现自己的 move constr / 运算符。
但是,BST 隐式继承自unique_ptr&lt;BSTknoop&lt;T&gt;&gt;,因此它必须拥有该类型的成员。假设我想保留继承,有没有什么巧妙的方法可以使这项工作? 我不能(或者可能不应该)实现一个复制constr / 操作符,因为那些是为unique_ptr 删除的,这也是真的吗?

  template<class T>
BST<T>::BST(BST<T>&& other){
    *this = move(other);
}

template<class T>
BST<T>& BST<T>::operator=(BST<T>&& other){
    cout<<"called move operator BST"<<endl;
    (*this).BSTknoopptr<T>::operator=(move(other));
    return *this;
}

编辑 3:使用我自己的 move constr / 运算符,临时树不再被正确填充。下面是我用于构建树的 add 代码。如果我省略了我自己的 move constr / 运算符,那么这是可行的。我需要的是一个类似于this-&gt;get()-&gt;links = move(tmp) 的操作。 类型this-&gt;get()-&gt;links =&gt; BST&lt;T&gt; tmp =&gt; BST&lt;T&gt; 这怎么行?它做了我需要的操作,为什么我不能通过自己编写操作来使用类似的东西来做*this = move(temptree)

template<class T>
void BST<T>::add(const T& value){
    if(value <= this->get()->sl){
        if(this->get()->links != nullptr){
            this->get()->links.add(value);
        }
        else {
            BST<T> tmp(new BSTknoop<T>(value));
            this->get()->links = move(tmp);
        }
    }
    else{
        if(this->get()->rechts != nullptr){
            this->get()->rechts.add(value);
        }
        else{
            BST<T> tmp(new BSTknoop<T>(value));
            this->get()->rechts = move(tmp);
        }
    }
}

【问题讨论】:

  • 显示swap(BST&amp;)operator=(BST&amp;&amp;) 成员的实现以及BST 模板类的定义会很有帮助。我们无法调试我们看不到的代码。
  • 顺便说一句,我想说你可能不应该从unique_ptr&lt;&gt;继承。相反,BST&lt;T&gt; 应该有一个 成员,该成员是unique_ptr&lt;&gt;
  • 当你决定继承 std::unique_ptr 时,我担心你开始陷入混乱。那就是疯狂。
  • @molbdnilo 我认为这被夸大了。如果出于特定目的这样做,这可能很好。从unique_ptr 继承是迄今为止实现某些其他智能指针的最快方式。例如。一个智能指针,它只是在复制时对指针进行深度复制。
  • @cdhowie 他们以前不在那里,我没有实现它们。我依靠继承的/默认生成的,这可能是一个错误。但是,就目前而言,它仍然无法正常工作。

标签: c++ c++11 move-semantics unique-ptr


【解决方案1】:

好吧,我犯了最愚蠢的错误。继承和伴随的移动语义没有错。我在istream&amp; BST&lt;T&gt;::lees(istream&amp; is) 函数中使用了 DATA_SET_LENGTH 预处理器指令。而我的最终数据集将包含一百万个项目。我用于测试的数据集仅包含 12 个。我忘记更改 DATA_SET_LENGTH 的值,所以 stoi(lijn) 崩溃了。

对于任何对解决方案感兴趣的人:

笔记

  • 您无需编写太多代码即可完成这项工作。
  • 可以自己写BST&lt;T&gt;&amp; operator=(BSTknoopptr&lt;T&gt;&amp;&amp;); 并且可以。但是,即使没有明确编写自己的代码,它也可以工作。
  • BST 类没有指针类型的字段,因此我们不会因为 unique_ptr 没有虚拟析构函数而遇到麻烦

最后一个问题(也许有人可以评论): 继承的运算符operator=(BSTknoopptr&lt;T&gt;&amp;&amp;),它的签名在BST&lt;T&gt; 中是什么样子的?主要是关于返回类型(如果它现在是BST&lt;T&gt; 的成员,它返回什么类型?)。

** TLDR:为什么我可以依赖unique_ptr&lt;BSTknoop&lt;T&gt;&gt; 的继承移动运算符/移动构造函数? **

bst.h

 #define DATA_SET_LENGTH 12

using namespace std;

template <class T>
class BST;

template <class T>
class BSTknoop;

template<class T>
using BSTknoopptr=std::unique_ptr<BSTknoop<T>>;



template <class T>
class BST:public BSTknoopptr<T>{
    using BSTknoopptr<T>::BSTknoopptr;
public:
    BST<T>& operator=(BST<T>&&) = default;
    BST<T>(BST<T>&&) = default;
    BST<T>()=default;
    //BST<T>& operator=(BSTknoopptr<T>&&);

    friend istream& operator>>(istream& is, BST<T>& bb){
        return bb.lees(is);
    }
    friend ostream& operator<<(ostream& os, const BST<T>& bb){
        //return bb.schrijflevelorder(os);
        return bb.schrijfKnoop(os);
    }
    void add(const T&);
//schrijf schrijft uit in een vorm die min of meer menselijk leesbaar is
    ostream& schrijf(ostream&);
    ostream& schrijfKnoop(ostream&) const;
    int aantalSleutels() const;
    istream& lees(istream&);
//schrijflevelorder schrijft uit in een vorm die door lees(...) kan gelezen worden.
    ostream& schrijflevelorder(ostream& os) const;
private:
};

template <class T>
class BSTknoop{
    friend class BST<T>;
public:
    BSTknoop() {}
    explicit BSTknoop(T _sl) : sl(_sl) {}
private:
    T sl;
    BST<T> links,rechts;
};


//template<class T>
//BST<T>& BST<T>::operator=(BSTknoopptr<T>&& other){
//    cout<<"called BST<T> move with BSTknoopptr r-value ref argument"<<endl;
//    (*this).BSTknoopptr<T>::operator=(move(other));
//    return *this;
//}


template<class T>
void BST<T>::add(const T& value){
    if(value <= this->get()->sl){
        if(this->get()->links != nullptr){
            this->get()->links.add(value);
        }
        else {
            //BSTknoopptr<T> tmp(new BSTknoop<T>(value)); if used with my own BST<T>& BST<T>::operator=(BSTknoopptr<T>&& other)
            BST<T> tmp(new BSTknoop<T>(value));
            this->get()->links = move(tmp);
        }
    }
    else{
        if(this->get()->rechts != nullptr){
            this->get()->rechts.add(value);
        }
        else{
            //BSTknoopptr<T> tmp(new BSTknoop<T>(value)); if used with my own BST<T>& BST<T>::operator=(BSTknoopptr<T>&& other)
            BST<T> tmp(new BSTknoop<T>(value));
            this->get()->rechts = move(tmp);
        }
    }
}

template<class T>
istream& BST<T>::lees(istream& is){
    string lijn;
    T knoopwaarde;
    getline(is,lijn);
    knoopwaarde = stoi(lijn);
    BST<T> temptree(new BSTknoop<T>());
    temptree->sl = knoopwaarde;

    for(int i=1; i<DATA_SET_LENGTH ; i++){
        getline(is,lijn);
        knoopwaarde=stoi(lijn);
        temptree.add(knoopwaarde);
        cout<<temptree;
    }
    *this = move(temptree);
    return is;
}

ma​​in.cpp

int main(){
    ifstream ifs;
    ifs.open("c.dat");
    if(ifs.is_open()){
        BST<int> tree;
        tree.lees(ifs);        
        tree.schrijfKnoop(cout);

    }
    else {
        cerr<<"failed to open file"<<endl;
        return -1;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-15
    • 2018-11-18
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    相关资源
    最近更新 更多