目前我在前向声明和模板功能方面遇到了令人沮丧的问题。我一直在尝试谷歌搜索并做一些修改但到目前为止没有任何工作。以下是代码片段:
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'
有人能告诉我这段代码出了什么问题吗?任何帮助表示赞赏..
答案 0 :(得分:5)
在findT
的定义中,您正在使用tss->tasks_
取消引用指向类型为TaskScheduler
的对象的指针,因此您需要完整定义结构,而不仅仅是前向声明可见在这个程序中。
struct TaskScheduler
的定义需要出现在findT
函数模板的定义之前。
答案 1 :(得分:1)
您在for-loop标头“tss-&gt; tasks_.begin()”中使用了TaskScheduler类。编译器不知道这个类是否有“tasks_”成员。
这不是您的模板的问题,任何函数,在头文件中内联都会导致相同的错误。该类的前向声明仅允许您声明该类的指针(或引用)或将此类对象作为参数传递。在完全定义类之前,您不能“使用”该类(调用其方法或获取成员数据)。
答案 2 :(得分:1)
因为您在findT函数中使用TaskScheduler的定义,所以有两个选项:
像这样:
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
关键字可能导致不是这种情况。该关键字只在一个编译器前端实现 - 我想知道为什么没有其他人实现它? [/讽刺]