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

时间:2010-11-21 19:36:24

标签: c++ templates

嘿伙计们。谢谢你点击。我正在努力与包括。基本上,我正在尝试创建一个模板类,其中有一个函数可以接收该模板的特定实例。我做了以下设计的例子来说明这一点。

让我们说,我有一个标有模板(通用)类型数据的个人世界。我有一个特定的个人,称为国王。所有个人都应该能够在国王面前跪下。一般来说,个人可以被标记为任何东西。国王用数字标记(第1,第2国王)。

错误

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){};
};

King.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);
    };
};

King.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

5 个答案:

答案 0 :(得分:3)

error: invalid use of incomplete type ‘struct King’

这意味着您只声明King而未定义它。您有循环包含问题(Individual.hKing.h互相包含)。 This article应该对这个问题有所了解。

答案 1 :(得分:3)

你的两个班级King和Individual紧密相连。

将它放在两个标题中是不行的,因为两者都需要彼此。

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

  1. 首先定义类Individual但不实现KneelBeforeTheKing,只需声明该函数。

  2. 然后定义King

  3. 然后实施上述方法。

  4. 但是你的设计可能都错了。例如,您的模板类有许多不依赖于篡改类型的方法,包括KneelBeforeTheKing,并且应该从模板中重构。

答案 2 :(得分:2)

嗯,这里的问题与模板没有关系。

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

反转课程怎么样?这样的事情(可能包含错误):

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

答案 3 :(得分:1)

您可以通过向类King添加另一个基类(例如NobleBase)来打破依赖关系:

struct NobleBase
{
  virtual void KneelBefore() = 0;
};


class King : public Individual<int>,
             public NobleBase

并改变方法
void KneelBeforeTheKing(King* king)
到这个 void KneelBeforeTheKing(NobleBase* king)

然后你需要包含NobleBase类的标题。

答案 4 :(得分:0)

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

void KneelBeforeTheKing(King* king)
{
    king->CommandToKneel();
    printf("Kneeling...\n");
};

正如Let_Me_Be所指出的,这是设计而不是语法的问题。实际上,国王应该决定个人何时下跪,所以任何对CommandToKneel()的调用都来自King本人,而个人的集合作为一个参数来指定谁被命令是有道理的。

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

然而,正如已经指出的那样,在其基本类型(即个人)的声明中对派生类型(即King)进行任何类型的引用都是错误的设计。基类应该不知道它的派生类型。