1

次の簡単なプログラムを 3 回繰り返してみました。これは、コンテナーとイテレーターのペアのクラスを作成するための非常に単純化された試みですが、不完全な型 (前方宣言) に関する問題に遭遇していました。すべてをテンプレート化すると、これが実際に可能であることがわかりました-ただし、実際にテンプレートパラメーターを使用した場合のみ! ( Google sparsetable codeを見てこれに気づきました。)

2番目が機能するのに3番目が機能しない理由を説明するヒントはありますか? (最初のものが機能しない理由はわかっています。コンパイラはコンテナーのメモリ レイアウトを知る必要があります。)

前もって感謝します。

// This doesn't work: invalid use of incomplete type.
#if 0
struct container;
struct iter {
  container &c;
  int *p;
  iter(container &c) : c(c), p(&c.value()) {}
};
struct container {
  int x;
  int &value() { return x; }
  iter begin() { return iter(*this); }
};
int main() {
  container c;
  c.begin();
  return 0;
}
#endif

// This *does* work.
template<typename T> struct container;
template<typename T> struct iter {
  container<T> &c;
  T *p;
  iter(container<T> &c) : c(c), p(&c.value()) {}
};
template<typename T> struct container {
  T x;
  T &value() { return x; }
  iter<T> begin() { return iter<T>(*this); }
};
int main() {
  container<int> c;
  c.begin();
  return 0;
};

// This doesn't work either.
#if 0
template<typename T> struct container;
template<typename T> struct iter {
  container<int> &c;
  int *p;
  iter(container<int> &c) : c(c), p(&c.value()) {}
};
template<typename T> struct container {
  int x;
  int &value() { return x; }
  iter<int> begin() { return iter<int>(*this); }
};
int main() {
  container<int> c;
  c.begin();
  return 0;
}
#endif
4

3 に答える 3

4

container1 つ目は、コピー操作を行っているため、 の定義が必要です。iterafterの定義のコンストラクタを定義すればcontainer大丈夫です。そう:

struct container;
struct iter {
  container &c;
  int *p;
  iter(container &c);
};

struct container {
  int x;
  int &value() { return x; }
  iter begin() { return iter(*this); }
};

iter::iter(container &c) : c(c), p(&c.value()) {}

int main() {
  container c;
  c.begin();
  return 0;
}

2 番目の例は、関数で実際にインスタンス化するまでクラスがないため、main機能します。その時までに、すべての型が定義されます。iterまたはテンプレート定義のいずれかを main の後に移動してみてくださいcontainer。エラーが発生します。

int3番目の例は、それが表示されるかどうかの専門化です。のテンプレート パラメータが使用されていないため、これはコンパイルされるはずiterです。特殊化の構文が少しずれています。ただし、適切なコンストラクターがないため、 のガベージのみが取得されxます。さらに、反復子はポインターによって適切にモデル化されます。の値を渡すthisことはあまり役に立ちません。通常、反復子は、個々のオブジェクトではなく、シーケンスに必要です。ただし、作成を止めるものは何もありません。

;また、関数本体の後には必要ありません。

于 2009-03-06T21:11:28.013 に答える
2

コンテナの定義後に iter::iter() を定義することで、テンプレートなしでこれを行うことができます。

struct container;

struct iter {
  container &c;
  int *p;
  iter(container &c);
};

struct container {
  int x;
  int &value() { return x; }
  iter begin() { return iter(*this); }
};

iter::iter(container &c)
    : c(c), p(&c.value()) {}

int main() {
  container c;
  c.begin();
  return 0;
}

テンプレートをインスタンス化すると、両方のクラスが完全に定義されるため、テンプレート バージョンが機能します。

于 2009-03-06T21:03:41.510 に答える
0

最初のケースでは、クラスが定義される前に Container クラスのメンバー関数にアクセスしようとしているため、これは機能しません。

2 番目のケースでは、テンプレートは特定の型で初めて使用されるときにインスタンス化されます。その時点で、Container クラスがメインで定義されているため、コンパイルされます。

3 番目のケースでは、循環参照があります。コンテナーは iter を使用し、 iter はコンテナーを使用するため、機能しません。

于 2009-03-06T21:06:43.640 に答える