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
//
//
};

这是我得到的错误消息,它也显示在代码中:./bt-taskscheduler.h:159: error: forward declaration of 'struct TaskScheduler' ./bt-taskscheduler.h:229: error:不完整类型'struct TaskScheduler'的无效使用</p>

谁能告诉我这段代码出了什么问题?任何帮助表示赞赏..

4

5 回答 5

5

findT您使用的定义中,tss->tasks_它取消引用指向类型对象的指针,TaskScheduler因此您需要结构的完整定义,而不仅仅是程序中此时可见的前向声明。

的定义struct TaskScheduler需要出现在findT函数模板的定义之前。

于 2009-08-11T10:35:09.507 回答
1

除了前向声明的麻烦之外,看起来您的 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 的定义,所以您有两个选择:

  • 将TaskScheduler的定义移到findT模板函数上面
  • 使 TaskScheduler 成为 findT 函数的第二个模板

像这样:

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

正如其他海报所提到的,您在TaskScheduler没有类型定义的情况下取消引用指向的指针,这将导致错误,就像在任何定义中一样。

您可能感到困惑的是,您的代码可能适用于某些编译器,甚至是现代编译器(我知道 MSVC 在这方面是不正确的,但我不知道它是否会接受上述代码*)。这些编译器没有正确实现所谓的两阶段名称查找

与某些编译器使用的更简单的形式相比,两阶段名称 loopkup 是模板中使用的名称查找更可预测的方法。在更简单的形式中,模板定义被解析和存储,仅在实例化时使用,并且从您实例化模板的点开始对模板中的所有名称执行名称查找。通过两阶段名称查找,模板中使用的名称分为依赖名称非依赖名称. 非依赖名称是编译器可以立即解析的名称 - 任何不直接或间接依赖模板参数的名称。定义模板时会立即处理这些名称。另一方面,从属名称无法立即解析;它们被存储,然后在执行实例化时在模板的上下文中查找,但也在模板被实例化的上下文中用于依赖于参数的查找

这是一个例子:

 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模板的实例化每个调用foo两次。第一次调用是不依赖的,所以编译器一看到就解析它。结果是它调用foo (int),应用转换,即使它稍后会找到更好的定义。这与 C++ 中的任何其他函数调用没有什么不同。棘手的一点是第二个调用,它是依赖的。第一个调用中baz调用bar<double>,后调用bar<qux>。实例化的bar尝试foo使用类型的对象进行调用T。在该double场景中,由于原语从不使用依赖于参数的查找,因此再次仅从 查找结果barfoo(int)找到。当与qux,但是,依赖于参数的查找在定义实例化上下文中都应用**,因此foo(qux)被称为。

这可能有点愚蠢,但它倾向于做正确的事。另外,我希望您真正理解这一点;它可能相当混乱。您需要阅读该 Wikipedia 链接才能完全理解。

* MSVC 可能会实现一种较小形式的两阶段名称查找,它确实可以正确解析非依赖名称,但它会考虑依赖名称模板之后的定义。我忘记了它是这样做还是完全省略了两阶段查找,而且我没有要检查的程序副本。

** 在几乎所有情况下,实例化上下文都包含定义上下文所做的每个声明。但是,有一个export关键字可能导致情况并非如此。该关键字仅在一个编译器前端实现 - 我想知道为什么没有其他人实现它?[/讽刺]

于 2009-08-11T11:10:47.813 回答