3

現在、前方宣言とテンプレート関数にイライラする問題があります。私はグーグルでいくつかの変更を試みてきましたが、これまでのところ何も機能していません。以下はコードのスニペットです。

class TaskScheduler; --> //forward declaration of ‘struct TaskScheduler’
//
//

class TaskEvent {
//
//
};

class HostTask {
//
//
};

template<class T> inline HostTask*
findT(TaskScheduler* tss, T* e)
{
    map<int, HostTask*>::iterator it;
    bool bEq = false;
    for(it = tss->tasks_.begin(); it != tss->tasks_.end(); it++) { --> //error: invalid use of incomplete type ‘struct TaskScheduler’
    if(dynamic_cast<TaskEvent*>(e))
        bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
    else if(dynamic_cast<HostTask*>(e))
        bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
    if(bEq) {
        return it->second;
    }
}
return NULL;
}
//

//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
friend HostTask* findT<TaskEvent>(TaskScheduler* tss, TaskEvent* e); //findT function is used here
//
//
};

コードにも示されているエラーメッセージは次のとおりです。不完全なタイプ「struct TaskScheduler」の無効な使用</p>

このコードで何が間違っているのか、誰か教えてもらえますか? どんな助けでも大歓迎です..

4

5 に答える 5

5

findT使用しているの定義ではtss->tasks_、型のオブジェクトへのポインターを逆参照するTaskSchedulerため、プログラムのこの時点で見える前方宣言だけでなく、構造体の完全な定義が必要です。

の定義は、関数テンプレートstruct TaskSchedulerの定義の前に表示される必要があります。findT

于 2009-08-11T10:35:09.507 に答える
1

forward 宣言の問題の次に、findT 関数が実際にはスケジューラ クラスのメンバ関数であるように見えます。スケジューラのデータ メンバを広範囲に使用します。

friendこれらのメンバーは非公開であるため、それらを公開して宣言にフォールバックする方法が必要です。

したがって、メンバーを公開するか、findT関数をメンバー関数にリファクタリングします。

テンプレート化されたメンバー関数にすることも問題ありません。そして、あなたは自動的に友達宣言を取り除きます。

//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
 public:
  template<class T> inline HostTask* findT(T* e) const
  {
    map<int, HostTask*>::iterator it;
    bool bEq = false;
    for(it = tasks_.begin(); it != tasks_.end(); it++) { 
       if(dynamic_cast<TaskEvent*>(e))
          bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
      else if(dynamic_cast<HostTask*>(e))
          bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
      if(bEq) {
          return it->second;
      }
    }
    return NULL;
  }


};
于 2009-08-11T11:36:40.273 に答える
1

for-loop ヘッダー "tss->tasks_.begin()" で TaskScheduler クラスを使用しています。コンパイラは、このクラスに「tasks_」メンバーがあるかどうかを知りません。

テンプレートの問題ではありません。ヘッダー ファイルにインライン化された関数は、同じエラーを引き起こします。クラスの前方宣言では、そのクラスへのポインター (または参照) を宣言するか、このクラス オブジェクトをパラメーターとして渡すことしかできません。クラスを完全に定義するまで、クラスを「使用」する (そのメソッドを呼び出す、またはメンバー データを取得する) ことはできません。

于 2009-08-11T10:38:44.413 に答える
1

findT 関数で TaskScheduler の定義を使用するため、次の 2 つのオプションがあります。

  • TaskScheduler の定義を findT テンプレート関数の上に移動します。
  • TaskScheduler を findT 関数の 2 番目のテンプレートにする

このような:

template< class U, class T> 
inline HostTask* findT( U* tss, T* e)
{
   //...
}
于 2009-08-11T10:45:27.677 に答える
0

他の投稿者が言及しているようにTaskScheduler、型の定義なしでへのポインターを逆参照しているため、他の定義と同様にエラーが発生します。

おそらく混乱しているのは、あなたのコードが一部のコンパイラで動作する可能性が高いということです (この点で MSVC が正しくないことは知っていますが、上記のコードを受け入れるかどうかはわかりません*)。これらのコンパイラは、いわゆる2 フェーズの名前検索を適切に実装していません。

2 フェーズの名前 loopkup は、一部のコンパイラで使用される単純な形式よりも、テンプレートで使用される名前検索のより予測可能な方法です。単純な形式では、テンプレート定義が解析され、インスタンス化されたときにのみ使用するために保存され、テンプレートをインスタンス化した時点から、テンプレート内のすべての名前に対して名前検索が実行されます。2 フェーズの名前検索により、テンプレート内で使用される名前は、依存する名前と依存しない名前に分類されます. 非依存の名前は、コンパイラがすぐに解決できる名前です。つまり、テンプレート パラメーターに直接的または間接的に依存しない名前です。これらの名前は、テンプレートを定義するとすぐに処理されます。一方、依存名はすぐには解決できません。それらは保存され、インスタンス化が実行されると、テンプレートのコンテキストでルックアップされますが、引数依存のルックアップのみでテンプレートがインスタンス化されたコンテキストでもルックアップされます。

次に例を示します。

 void foo (int);
 template <typename T> void bar(T t) {
   foo(1.0);
   foo(t);
 }
 void foo (double);

 struct qux {};
 void foo (qux);

 void baz () {
   bar (1.0);
   qux q;
   bar (q);
 }

注意: メタ構文名の順序が間違っていることはわかっています。申し訳quxありませんが、最後に追加したので、わざわざコメントを書き直すことができませんでした。

barテンプレートのインスタンス化は、それぞれfoo2 回呼び出します。最初の呼び出しは非依存であるため、コンパイラはそれを確認するとすぐに解決します。その結果foo (int)、後でより適切な定義が見つかるにもかかわらず、変換を適用して が呼び出されます。これは、C++ の他の関数呼び出しと同じです。注意が必要なのは、依存関係にある 2 番目の呼び出しです。呼び出しの最初の呼び出しbazbar<double>後者の呼び出しbar<qux>。インスタンス化された は、タイプ のオブジェクトでbar呼び出しを試みます。シナリオでは、プリミティブは引数依存のルックアップを使用しないため、結果は再び からのみルックアップされ、見つかります。で呼び出した場合fooTdoublebarfoo(int)quxただし、引数依存のルックアップは、定義コンテキストインスタンス化コンテキストの両方で適用される**ため、foo(qux)呼び出されます。

少しばかげているかもしれませんが、正しいことをする傾向があります。また、実際にそれを理解していただければ幸いです。かなり混乱する可能性があります。完全に理解するには、ウィキペディアのリンクを読む必要があります。

* MSVC は、依存しない名前を正しく解決する、より少ない形式の 2 フェーズの名前検索を実装する場合がありますが、依存する名前のテンプレートの後に定義が考慮されます。これを行うのか、2 フェーズ ルックアップを完全に省略するのかを忘れてしまい、チェックするプログラムのコピーがありません。

** ほとんどの場合、インスタンス化コンテキストには、定義コンテキストが行うすべての宣言が含まれます。ただし、exportこれが当てはまらないキーワードがあります。そのキーワードは、1 つのコンパイラ フロントエンドでのみ実装されています。他の誰も実装していないのはなぜでしょうか? [/皮肉]

于 2009-08-11T11:10:47.813 に答える