前向声明和模板功能错误

时间:2009-08-11 10:29:39

标签: c++ forward-declaration

目前我在前向声明和模板功能方面遇到了令人沮丧的问题。我一直在尝试谷歌搜索并做一些修改但到目前为止没有任何工作。以下是代码片段:

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:错误:'struct TaskScheduler'的前向声明 ./bt-taskscheduler.h:229:错误:无效使用不完整类型'struct TaskScheduler'

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

5 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:1)

您在for-loop标头“tss-&gt; tasks_.begin()”中使用了TaskScheduler类。编译器不知道这个类是否有“tasks_”成员。

这不是您的模板的问题,任何函数,在头文件中内联都会导致相同的错误。该类的前向声明仅允许您声明该类的指针(或引用)或将此类对象作为参数传递。在完全定义类之前,您不能“使用”该类(调用其方法或获取成员数据)。

答案 2 :(得分:1)

因为您在findT函数中使用TaskScheduler的定义,所以有两个选项:

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

像这样:

template< class U, class T> 
inline HostTask* findT( U* tss, T* e)
{
   //...
}

答案 3 :(得分: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;
  }


};

答案 4 :(得分:0)

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

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

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

以下是一个例子:

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

N.B。我知道我的错误顺序得到了metasyntactic名称。我道歉,但我最后添加了qux,并且无法重写我的评论。

bar模板的实例化,每次调用foo两次。第一个调用是非依赖的,因此编译器会在看到它时立即解析它。结果是它调用foo (int),应用转换,即使稍后会找到更好的定义。这与C ++中的任何其他函数调用没有什么不同。棘手的一点是第二次调用,这是依赖的。 baz中的第一个电话呼叫bar<double>,后者呼叫bar<qux>。实例化的bar尝试使用foo类型的对象调用T。在double场景中,由于原语从不使用参数依赖查找,因此仅从bar再次查找结果,并找到foo(int)。但是,使用qux调用时,依赖于参数的查找将在定义实例化上下文**中应用,因此调用foo(qux)

这可能有点愚蠢,但它倾向于做正确的事。另外,我希望你真的明白了;它可能相当令人困惑。您需要阅读Wikipedia链接才能完全理解。

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

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