前向声明和模板函数错误

发布于 2024-08-01 23:52:07 字数 1237 浏览 4 评论 0原文

目前,我在前向声明和模板函数方面遇到了令人沮丧的问题。 我一直在尝试谷歌搜索并进行一些修改,但到目前为止没有任何效果。 下面是代码片段:

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”

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

Currently I have a frustrating problem with forward declaration and template function. I have been trying to googling and do some modification but nothing has worked so far. Below is the snippet of the code:

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

Here is the error message that I've got which is shown in the code as well:
./bt-taskscheduler.h:159: error: forward declaration of ‘struct TaskScheduler’
./bt-taskscheduler.h:229: error: invalid use of incomplete type ‘struct TaskScheduler’

Could anybody show me what is going wrong in this code? Any help is appreciated..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

分開簡單 2024-08-08 23:52:07

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

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

In the definition of findT you are using tss->tasks_ which dereferences a pointer to an object of type TaskScheduler so you need a full definition of the struct, not just a forward declaration visible at this point in the program.

The definition of struct TaskScheduler needs to appear before the definition of the findT function template.

北斗星光 2024-08-08 23:52:07

您正在 for 循环标头“tss->tasks_.begin()”中使用 TaskScheduler 类。 编译器不知道这个类是否有“tasks_”成员。

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

You are using the TaskScheduler class in your for-loop header "tss->tasks_.begin()". Compiler does not know, whether this class have "tasks_" member or not.

It is not the problem with your templates, any function, inlined in the header file will cause the same error. Forward declaration of the class only allows you to declare pointers (or references) to that class or pass this class objects as a parameters. You cannot "use" the class (call its methods or get the member data), until you fully define your class.

不知在何时 2024-08-08 23:52:07

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

  • 将 TaskScheduler 的定义移至 findT 模板函数上方
  • 使 TaskScheduler 成为 findT 函数的第二个模板,

如下所示:

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

Because you use the definition of TaskScheduler in the findT functions, you have two options:

  • Move the definition of TaskScheduler above the findT template function
  • Make TaskScheduler a second template of of the findT function

Like this:

template< class U, class T> 
inline HostTask* findT( U* tss, T* e)
{
   //...
}
小…红帽 2024-08-08 23:52:07

除了前向声明的麻烦之外,看起来您的 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;
  }


};

Next to trouble with the forward declaration, it looks as if your findT function should actually be a member function of the scheduler class: it makes extensive use of the scheduler's data members.

These members are private, so you need a way to publish them, and fall back onto the friend declaration.

So either you make the members public, or, better, you refactor the findT function into a member function.

There's no problem in making it a templated member function, either. And you will automatically get rid of the friend declaration.

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


};
自演自醉 2024-08-08 23:52:07

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

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

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

这是一个例子:

 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,后者调用 bar。 实例化的 bar 尝试使用 T 类型的对象调用 foo。 在 double 场景中,由于基元从不使用依赖参数的查找,因此再次仅从 barfoo(int) 查找结果> 被发现。 然而,当使用 qux 调用时,依赖于参数的查找将同时应用于定义实例化上下文**,因此 foo(qux)叫做。

它可能有点愚蠢,但它往往会做正确的事。 另外,我希望你能真正理解这一点; 这可能相当令人困惑。 您需要阅读该维基百科链接才能完全理解。

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

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

As other posters have mentioned, you are dereferencing a pointer to TaskScheduler without a definition of the type, which will cause an error just as it would in any definition.

What you are probably confused about is that your code likely works on some compilers, even modern ones (I know MSVC is incorrect in this regard, but I do not know if it will accept the above code*). These compilers do not properly implement what is known as two-phase name lookup.

Two-phase name loopkup is a more predictable method of name lookup used in templates than the simpler form used by some compilers. In the simpler form, the template definition is parsed and stored for use only when it's instantiated, and name lookup is performed on all names in the template from the point at which you instantiate the template. With two-phase name lookup, names used within a template are sorted into dependent names and non-dependent names. Non-dependent names are names that the compiler can resolve immediately - any name that doesn't rely on a template parameter, directly or indirectly. These names are processed immediately when you define the template. Dependent names, on the other hand, cannot be resolved immediately; they are stored and then, when instantiation is performed, looked up in the template's context, but also in the context in which the template was instantiated for argument-dependent lookup only.

Here's an example:

 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. I know I got the metasyntactic names in the wrong order. I apologize, but I added qux last and couldn't be bothered to rewrite my comment.

The instantiations of the bar template each call foo twice. The first call is non-dependent, so the compiler resolves it as soon as it sees it. The result is that it calls foo (int), applying a conversion, even though it will later find a better definition. This is no different from any other function call in C++. The tricky bit comes with the second call, which is dependent. The first call in baz calls bar<double>, the latter calls bar<qux>. The instantiated bar attempts to call foo with an object of type T. In the double scenario, since primitives never use argument-depedent lookup, the result is once again looked up only from bar, and foo(int) is found. When called with qux, however, argument-dependent lookup is applied both in the the definition and instantiation context**, so foo(qux) is called.

It can be a tad stupid, but it tends to Do The Right Thing. Also, I hope you actually understood that; it can be rather confusing. You'll need to read that Wikipedia link to understand fully.

* MSVC may implement a lesser form of two-phase name lookup where it does resolve non-dependent names correctly, but it takes into account definitions after the template for dependent names. I forget whether it does this or omits two-phase lookup entirely and I don't have a copy of the program to check.

** In nearly every case, the instantiation context includes every declaration the definition context does. There is, however, the export keyword which can cause this not to be the case. That keyword is only implemented in one compiler frontend - I wonder why nobody else has implemented it? [/sarcasm]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文