[英]Forward Declarations/Includes with Template Classes - “invalid use of incomplete type”
我正在尝试创建一个模板类,其中有一个函数可以接收该模板的特定实例。 我做了以下人为的例子来说明这一点。
比方说,我有一个用模板化(通用)类型的数据标记的个人世界。 我有一个特定的个体,称为国王。 并且所有个人都应该能够在国王面前下跪。 一般来说,个人可以被标记为任何东西。 国王用数字标记(第一个、第二个国王)。
错误
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
Individual.h(Individual.cpp 为空)
//Individual.h
#pragma once
class King;
#include "King.h"
#include <cstdlib>
#include <cstdio>
template <typename Data> class Individual
{
protected:
Data d;
public:
void Breathe()
{
printf("Breathing...\n");
};
void KneelBeforeTheKing(King* king)
{
king->CommandToKneel();
printf("Kneeling...\n");
};
Individual(Data a_d):d(a_d){};
};
王.h
//King.h
#pragma once
#include "Individual.h"
#include <cstdlib>
#include <cstdio>
class King : public Individual<int>
{
protected:
void CommandToKneel();
public:
King(int a_d):
Individual<int>(a_d)
{
printf("I am the No. %d King\n", d);
};
};
国王.cpp
//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'
这意味着您只声明了King
而没有定义它。 您有循环包含问题( Individual.h
和King.h
包含)。 这篇文章应该对这个问题有所了解。
你的两个类 King 和 Individual 是非常紧密耦合的。
将它放在两个这样的标题中是行不通的,因为两者都需要彼此。
如果您的课程必须这样设计,那么:
首先定义类Individual但不实现KneelBeforeTheKing,只需声明该函数。
然后定义国王
然后实现上面的方法。
但是,您的设计可能完全错误。 例如,您的模板类有许多不依赖于 tamped 类型的方法,包括 KneelBeforeTheKing,应该从模板中重构出来。
嗯,这里的问题与模板并不真正相关。
您正在调用基类中后代的特殊功能。 这几乎总是一个糟糕的设计标志。
颠倒类怎么样? 像这样(可能包含错误):
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);
}
您可以通过向类 King 添加另一个基类(例如 NobleBase)来打破依赖关系:
struct NobleBase
{
virtual void KneelBefore() = 0;
};
class King : public Individual<int>,
public NobleBase
并改变方法void KneelBeforeTheKing(King* king)
对此void KneelBeforeTheKing(NobleBase* king)
然后您需要包含 NobleBase 类的标头。
除了语法和编译器错误,我认为最大的问题是你的个人告诉国王告诉自己下跪。
void KneelBeforeTheKing(King* king)
{
king->CommandToKneel();
printf("Kneeling...\n");
};
正如 Let_Me_Be 指出的那样,这是设计问题而不是语法问题。 实际上,国王应该决定个人何时下跪,因此对 CommandToKneel() 的任何调用都来自国王本人是有道理的,并且个人集作为参数提供,以指定谁被命令。
语法问题是您正在尝试使用尚未定义的功能。 King 的所有前向声明所做的就是告诉 Individual 的头文件有一个名为 King 的类型。 那时编译器对 King 的成员一无所知,因为您没有包含声明这些成员的头文件,也没有在使用 King 之前的任何地方定义它。
然而,正如已经指出的那样,在其基类型(即个人)的声明中对派生类型(即 King)进行任何类型的引用都是糟糕的设计。 基类应该对其派生类型一无所知。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.