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
相互包含)。你的两个职业国王和个人是紧密结合的
把它放在两个这样的标题中是行不通的,因为两者都需要对方
如果您的课程必须以这种方式设计,那么:
然而,你的设计可能全错了。例如,您的模板类有许多不依赖于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");
};