2

解決しました!下記参照

だから、私はいくつかの単純なデータ構造を実行し、それらをいじることによって C++11 を学ぼうとしています。生のポインターを使用して、次の BST の例と同様のことをnew行いdelete、正常に動作しました。それから、より漏れのない方法でそれをやりたかった.

// tree.cpp
// 
// 

#include <iostream>
#include <memory>

/* DECLARATIONS */
template <typename T>
struct Tree {
  // members
  T data;
  std::unique_ptr<Tree<T> > left;
  std::unique_ptr<Tree<T> > right;
  // methods
  Tree (T arg);
  ~Tree () = default;
  void insert (Tree<T> child);
  void insert (T arg);
  void print (void);
};

template <typename T>
Tree<T>::Tree (T arg) {
  data = arg;
  left = nullptr;
  right = nullptr;
}

template <typename T>
void Tree<T>::insert (Tree<T> child) {
  if (child.data < data) {
    if (left) {
      left->insert(child);
    } else {
      left = &child;
    }
  } else {
    if (right) {
      right->insert(child);
    } else {
      right = &child;
    }
  }
}

template <typename T>
void Tree<T>::insert (T arg) {
  Tree<T> child (arg);
  this->insert(child);
}

template <typename T>
void Tree<T>::print (void) {
  if (left) {
    left->print();
  }
  std::cout << data;
  if (right) {
    right->print();
  }
}

int main (void) {
  Tree<int> root (0);
  root.insert(3);
  root.insert(-3);
  root.insert(-2);
  root.insert(2);
  root.insert(11);
  root.print();
  return 0;
}

ただし、clang ++から得られるエラーは理解できません。

$ clang++ -std=c++11 tree.cpp

tree_new.cpp:50:16: error: call to deleted constructor of 'Tree<int>'
  this->insert(child);
               ^~~~~
tree_new.cpp:66:8: note: in instantiation of member function 'Tree<int>::insert'         requested here
   root.insert(3);
   ^
tree_new.cpp:10:8: note: function has been explicitly marked deleted here
struct Tree {
       ^
tree_new.cpp:18:24: note: passing argument to parameter 'child' here
  void insert (Tree<T> child);
                       ^
tree_new.cpp:34:20: error: call to deleted constructor of 'Tree<int>'
  left->insert(child);
               ^~~~~
tree_new.cpp:50:9: note: in instantiation of member function 'Tree<int>::insert'requested here
  this->insert(child);
        ^
tree_new.cpp:66:8: note: in instantiation of member function 'Tree<int>::insert' requested here
  root.insert(3);
       ^
tree_new.cpp:10:8: note: function has been explicitly marked deleted here
struct Tree {
       ^
tree_new.cpp:18:24: note: passing argument to parameter 'child' here
  void insert (Tree<T> child);
                       ^
2 errors generated.

を宣言したときにコンストラクターを明示的に削除したと表示されるのはなぜstructですか? コンストラクターも明示的に定義しました。また、スコーピング/所有権の失敗に関するコメントをいただければ幸いです。とにかく、これは私がやったようにはうまくいかないだろうと確信しています。

解決

MSDNからの次のリンクは、 s の使用方法を明確にしunique_ptrました。

unique_ptr問題の最初の説明 (暗黙的に a をメンバーとして使用すると (コンパイラーは「明示的に」と言いますが...)、クラスのコピー コンストラクターを削除します)、そして実際に Tree がまだ移動可能であることを指摘してくれた BatchyX に感謝します。

そのMSDNの記事で言及されていることstd::move()は、その引数の右辺値を返すことです。

適切に変更されたコードを次に示します (明らかに変更された宣言を除く)。std::forward を使用することでまだいくつかの最適化が可能である可能性があることに注意してください。ただし、これは少なくとも正しくコンパイルおよび実行されるようです。

template <typename T>
void Tree<T>::insert (std::unique_ptr<Tree<T> >&& pchild) {
  if (pchild->data < data) {
    if (left) {
      // recurse, but must match on the rvalue signature
      left->insert(std::move(pchild));
    } else {
      // invokes the move constructor for left instead of its copy constructor
      left = std::move(pchild);
    }
  } else {
    if (right) {
      right->insert(std::move(pchild));
    } else {
      right = std::move(pchild);
    }
  }
}

template <typename T>
void Tree<T>::insert (T arg) {
  // what is inside the insert(...) is an rvalue.
  this->insert(std::unique_ptr<Tree<T> >(new Tree<T> (arg)));
}
4

4 に答える 4

6

std::unique_ptrはコピー可能ではなく、 a を含むクラスunique_ptrもコピー可能ではありません。つまり、 はコピー可能でstruct Treeはありません。への引数:

void Tree<T>::insert (Tree<T> child) {

引数を値で取っています。と:

template <typename T>
void Tree<T>::insert (T arg) {
  Tree<T> child (arg);
  this->insert(child);
}

コピー コンストラクターが必要です。これを修正するには、struct Tree移動可能にします。


以下が存在するため、メモTreeは移動できません( BatchyXのコメントとは対照的に)。

~Tree () = default;

これはユーザー宣言のデストラクタであり、c++11 標準 (ドラフト n3337) のセクション12.8 クラス オブジェクトのコピーと移動(ポイント 9) から:

クラス X の定義でムーブ コンストラクターが明示的に宣言されていない場合、1 つが暗黙的にデフォルトとして宣言されます。

  • X にはユーザー宣言のコピー コンストラクターがありません。
  • X には、ユーザー宣言のコピー代入演算子がありません。
  • X には、ユーザー宣言の移動代入演算子がありません。
  • X にはユーザー宣言のデストラクタがありません。
  • 移動コンストラクターは、暗黙的に削除済みとして定義されません。

(移動メンバーの暗黙の生成について確信が持てなかったので、この質問をして確信を持ってもらいました)。移動可能にするには、次のいずれかを行います。

  • ユーザーが宣言したデストラクタを削除するか、または
  • 移動コンストラクターと移動代入演算子を定義する
于 2013-06-01T17:28:49.343 に答える
3

注: フィールド 'left' のコピー コンストラクターが削除されているため、'Tree' のコピー コンストラクターは暗黙的に削除されます。

std::unique_ptrコピーコンストラクターがありません

于 2013-06-01T17:28:31.303 に答える
1

コンパイラはこれについて警告しないかもしれません (おそらく、さらに警告を有効にする必要があります) が、これは機能しません:

template <typename T>
void Tree<T>::insert (Tree<T> child) {
  // ...
  left = &child;;
}

このコードは一時変数のアドレスを取得し、unique_ptr. これは間違っています。 unique_ptr<A>で割り当てられたオブジェクトへのポインタを格納するためのものですnew。その目的の 1 つは、破棄時にそれらを削除して、メモリ リークが発生しないようにすることです。

ここでchildは、関数を終了するときに破棄される一時的なものです。その意味leftには、スタック上にあるものを指すポインターが含まれます。これにより、ランダムな破損が発生する可能性があり、Treeオブジェクトが破棄されると最終的にクラッシュします。

child参照 (右辺値または左辺値) でnewあっても、そうではない可能性があるため (コードでは決してそうではありません)、そうであったとしても、おそらくオブジェクトはすでに別の場所 (別の などunique_ptr) で管理されているため、いじってはいけません。

代わりに、オブジェクトにメモリを割り当てTreeて内部に保存する必要がありますleft

left = new Tree<T>(child);

insertコピー可能にする必要がある引数を整理する必要がありますTreeが (ヒント: 代わりに右辺値参照を使用してください: Tree<T>&& child)、コンパイラがこれらの種類のエラーを検出できないため、この問題はさらに悪化します。

于 2013-06-01T18:17:37.603 に答える
0

特殊なコンストラクター

Tree(T arg);

コンパイラが生成したデフォルト コンストラクタをオーバーライドします。したがって、自分で含める必要があります。

T() = default;
于 2013-06-01T17:27:10.150 に答える