Форвардные объявления / Включения с шаблонными классами - «недопустимое использование неполного типа» - PullRequest
4 голосов
/ 21 ноября 2010

Привет, ребята. Спасибо, что нажали. Я борюсь с включает. По сути, я пытаюсь создать класс шаблона, в котором есть функция, которая принимает конкретный экземпляр этого шаблона. Я сделал следующий надуманный пример, чтобы проиллюстрировать это.

Скажем, у меня есть мир людей, помеченных шаблонным (универсальным) типом данных. У меня есть конкретный человек, называемый королем. И все люди должны быть в состоянии встать на колени перед королем. Физические лица, как правило, могут быть помечены как что-либо. Короли отмечены числами (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");
}

Makefile

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 ]

3 голосов
/ 21 ноября 2010

Ваши два класса King и Individual очень тесно связаны.

Наличие такого заголовка в двух заголовках не сработает, потому что оба нужны друг другу.

Если ваши классы должны быть разработаны таким образом, то:

  1. Сначала определите класс Individual, но не реализуйте KneelBeforeTheKing, просто объявите эту функцию.

  2. Затем определите короля

  3. Затем выполните описанный выше метод.

Однако ваш дизайн, вероятно, все неправильно. Например, в вашем шаблонном классе есть много методов, которые не зависят от тамплированного типа, включая KneelBeforeTheKing, и они должны быть переработаны из шаблона.

3 голосов
/ 21 ноября 2010
error: invalid use of incomplete type ‘struct King’

Это означает, что вы только объявили King без определения его. У вас есть круговые проблемы включения (Individual.h и King.h включают друг друга). Эта статья должна пролить свет на этот вопрос.

2 голосов
/ 21 ноября 2010

Ну, проблема здесь не совсем связана с шаблонами.

Вы вызываете специализированную функциональность потомка в базовом классе. Это почти всегда плохой знак дизайна.

А как насчет инверсии классов? Примерно так (может содержать ошибки):

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);
}
1 голос
/ 22 ноября 2010

Вы можете сломать зависимость, добавив другой базовый класс (например, NobleBase) к классу King:

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


class King : public Individual<int>,
             public NobleBase

и изменив метод
void KneelBeforeTheKing(King* king)
на этот
void KneelBeforeTheKing(NobleBase* king)

Затем необходимо включить заголовок для класса NobleBase.

0 голосов
/ 22 ноября 2010

Помимо синтаксических ошибок и ошибок компилятора, я думаю, что самая большая проблема заключается в том, что ваш Индивид велит Королю сказать себе, что нужно встать на колени.

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

Как указал Let_Me_Be, это проблема дизайна, а не синтаксиса,На самом деле, король должен решить, когда отдельные люди становятся на колени, поэтому имеет смысл, чтобы любые вызовы CommandToKneel () исходили от самого короля, а набор отдельных лиц указывался в качестве аргумента для указания того, кому приказано.

Синтаксическая проблема заключается в том, что вы пытаетесь использовать функциональность, которая не была определена.Все предварительное объявление для King - это сообщить заголовочному файлу для Individual, что существует тип с именем King.На этом этапе компилятор ничего не знает об элементах King, поскольку вы не включили заголовочный файл, объявляющий эти элементы, и не определили King нигде до его использования.

Однако, как уже указывалось,наличие какой-либо ссылки на производный тип (например, King) в объявлении его базового типа (например, Individual) является плохим дизайном.Базовый класс не должен ничего знать о его производных типах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...