C++ 使用模板类转发声明/包含-“;“不完整类型的无效使用”;

C++ 使用模板类转发声明/包含-“;“不完整类型的无效使用”;,c++,templates,C++,Templates,我正在尝试创建一个模板类,其中有一个函数接收该模板的特定实例。我用以下人为的例子来说明这一点 比方说,我有一个用模板化(通用)数据类型标记的个人世界。我有一个特别的人,叫做国王。所有人都应该能够在国王面前下跪。一般来说,个人可以被标记为任何东西。国王用数字标记(第一、第二国王) 错误 g++ -g -O2 -Wall -Wno-sign-compare -Iinclude -DHAVE_CONFIG_H -c -o Individual.o Individual.cpp g++ -g -O

我正在尝试创建一个模板类,其中有一个函数接收该模板的特定实例。我用以下人为的例子来说明这一点

比方说,我有一个用模板化(通用)数据类型标记的个人世界。我有一个特别的人,叫做国王。所有人都应该能够在国王面前下跪。一般来说,个人可以被标记为任何东西。国王用数字标记(第一、第二国王)

错误

g++ -g -O2 -Wall -Wno-sign-compare -Iinclude -DHAVE_CONFIG_H    -c -o Individual.o Individual.cpp
g++ -g -O2 -Wall -Wno-sign-compare -Iinclude -DHAVE_CONFIG_H    -c -o King.o King.cpp
In file included from King.h:3,
                 from King.cpp:2:
Individual.h: In member function ‘void Individual<Data>::KneelBeforeTheKing(King*)’:
Individual.h:21: error: invalid use of incomplete type ‘struct King’
Individual.h:2: error: forward declaration of ‘struct King’
make: *** [King.o] Error 1

这意味着您只声明了
King
,而没有定义它。您有循环包含问题(
Individual.h
King.h
相互包含)。你的两个职业国王和个人是紧密结合的

把它放在两个这样的标题中是行不通的,因为两者都需要对方

如果您的课程必须以这种方式设计,那么:

  • 首先定义类Individual,但不实现KneelBefortheking,只声明该函数

  • 然后定义国王

  • 然后实现上述方法


  • 然而,你的设计可能全错了。例如,您的模板类有许多不依赖于tamplated类型的方法,包括KneelBeforeTheKing,应该从模板中重构出来。

    好吧,这里的问题实际上与模板无关

    您正在基类中调用子体的专用功能。这几乎总是一个糟糕的设计标志

    把这些类颠倒过来怎么样?类似这样的内容(可能包含错误):

    模板
    类人:PersonType
    {
    公众:
    void command_to_kneel(){PersonType::command_to_kneel();}
    无效跪在国王(人*国王){king->命令跪下()}
    无效呼吸(){}
    };
    int main()
    {
    人王;
    个人骑士;
    骑士。跪在国王(和国王)面前;
    }
    
    您可以通过向类添加另一个基类(例如NobleBase)来打破依赖关系:

    struct NobleBase
    {
      virtual void KneelBefore() = 0;
    };
    
    
    class King : public Individual<int>,
                 public NobleBase
    
    struct NobleBase
    {
    虚空KneelBefore()=0;
    };
    班王:公共个人,
    公共基地
    
    并更改方法
    void kneelbfortheking(国王*国王)

    对此
    void kneelboretheking(NobleBase*king)


    然后您需要包含NobleBase类的头。

    除了语法和编译器错误之外,我认为最大的问题是您的个人告诉国王让自己跪下

    void KneelBeforeTheKing(King* king)
    {
        king->CommandToKneel();
        printf("Kneeling...\n");
    };
    
    正如我要指出的,这是一个设计问题,而不是语法问题。事实上,国王应该决定个人何时下跪,因此对CommandToKneel()的任何调用都是国王自己发出的,而这组个人应该作为一个参数来指定谁被命令

    语法问题是您试图使用尚未定义的功能。King的所有转发声明都是告诉Individual的头文件有一个名为King的类型。此时,编译器对King的成员一无所知,因为您没有包含声明这些成员的头文件,也没有在使用King之前在任何地方定义它


    然而,正如已经指出的,在其基类型(即个体)的声明中对派生类型(即King)进行任何形式的引用都是糟糕的设计。基类应该对其派生类型一无所知。

    我认为我的设计不正确,因为我遇到的问题似乎是人为的。我需要一个模板化类的原因是存储模板化数据(一个国王被标记为第n个国王,一个人被标记为一个字符串作为他们的名字,另一种类型的人可以被标记为一个人号,“即person#234”)。有没有一种方法可以在不创建模板类的情况下存储模板数据?@TheChariot:默认情况下(除非您在代码中另有说明),模板实例化是不相关的类。如果您想在语言中表示一个概念(<代码>个人>代码>),并且与该概念一致的不同实体共享一个共同的功能考虑使用继承。基类没有任何存储的数据,每个派生类都会在您的设计中添加特定的数据(一个普通人的字符串,一个无名人的数字…),同时它们共享一些方法,
    Individual
    Individual
    不能通过同一接口传递。虽然在基类中调用子代通常是一种糟糕的设计,但错误并非如此。编译器抱怨
    King
    在实例化
    Individual
    时不完整,这不是基类调用派生方法的问题,而是派生方法在调用位置尚未定义。请注意,gamedev.net上的文章链接已断开。这篇文章似乎已被删除。
    //King.cpp
    #include "King.h"
    #include <string>
    
    int main(int argc, char** argv)
    {
        Individual<std::string> person("Townsperson");
        King* king = new King(1);
        king->Breathe();
        person.Breathe();
        person.KneelBeforeTheKing(king);
        
    }
    
    void King::CommandToKneel()
    {
        printf("Kneel before me!\n");
    }
    
    CXX = g++
    CXXFLAGS = -g -O2 -Wall -Wno-sign-compare -Iinclude -DHAVE_CONFIG_H 
    OBJS = Individual.o King.o
    
    test: $(OBJS)
        $(CXX) -o $@ $^
    
    clean:
        rm -rf $(OBJS) test
        
    all: test
    
    error: invalid use of incomplete type ‘struct King’
    
    template < typename PersonType >
    class Person : PersonType
    {
        public:
            void command_to_kneel() { PersonType::command_to_kneel(); }
            void kneel_before_king(Person<King>* king) { king->command_to_kneel(); }
            void breathe() {  } 
    };
    
    int main()
    {
        Person<King> king;
        Person<Knight> knight;
        knight.kneel_before_king(&king);
    }
    
    struct NobleBase
    {
      virtual void KneelBefore() = 0;
    };
    
    
    class King : public Individual<int>,
                 public NobleBase
    
    void KneelBeforeTheKing(King* king)
    {
        king->CommandToKneel();
        printf("Kneeling...\n");
    };