Проблема связанного с суперклассами - PullRequest
2 голосов
/ 16 октября 2008

Я пытаюсь создать класс C ++ с шаблонным суперклассом. Идея в том, что я могу легко создать множество похожих подклассов из нескольких суперклассов, которые имеют схожие характеристики.

Я выполнил проблемный код следующим образом:

template_test.h:

template<class BaseClass>
class Templated : public BaseClass
    {
public:
    Templated(int a);
    virtual int Foo();
    };

class Base
    {
protected:
    Base(int a);
public:
    virtual int Foo() = 0;
protected:
    int b;
    };

template_test.cpp:

#include "template_test.h"

Base::Base(int a)
    : b(a+1)
    {
    }

template<class BaseClass>
Templated<BaseClass>::Templated(int a)
    : BaseClass(a)
    {
    }

template<class BaseClass>
int Templated<BaseClass>::Foo()
    {
    return this->b;
    }

main.cpp:

#include "template_test.h"

int main()
    {
    Templated<Base> test(1);
    return test.Foo();
    }

Когда я строю код, я получаю ошибки компоновщика, говоря, что символы Templated<Base>::Templated(int) и Templated<Base>::Foo() не могут быть найдены.

Быстрый Google предлагает, что добавление следующего к main.cpp решит проблему:

template<> Templated<Base>::Templated(int a);
template<> int Templated<Base>::Foo();

Но это не решает проблему. Добавление строк в main.cpp также не работает. (Хотя, что интересно, добавление их к обоим приводит к ошибкам «кратно определенного символа» от компоновщика, поэтому они должны что-то делать ...)

Однако, , размещение всего кода в одном исходном файле решает проблему. Хотя это было бы неплохо для вышеприведенного примера, реальное приложение, на которое я смотрю, стало бы неуправляемым очень быстро, если бы мне пришлось поместить весь лот в один файл cpp.

Кто-нибудь знает, возможно ли то, что я делаю? (Как) я могу решить свои ошибки компоновщика?

Я бы предположил, что мог бы встроить все методы в class Templated, и это сработало бы, но это тоже не кажется идеальным.

Ответы [ 4 ]

4 голосов
/ 16 октября 2008

Для шаблонных классов определения должны быть доступны для каждой единицы перевода, которая его использует. Определения могут находиться в отдельном файле, обычно с расширением .inl или .tcc; заголовочный файл #include с этим файлом внизу. Таким образом, хотя он находится в отдельном файле, он все равно #include d для каждой единицы перевода; оно не может быть автономным.

Итак, для вашего примера, переименуйте template_test.cpp в template_test.inl (или template_test.tcc, или что-то еще), затем #include "template_test.inl" (или что-то еще) внизу template_test.h, прямо перед #endif охранника включения.

Надеюсь, это поможет!

2 голосов
/ 16 октября 2008

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

Затем, когда вы ссылаетесь, main.cpp говорит, что ему нужны эти функции, но они никогда не были скомпилированы в объектные файлы, поэтому компоновщик не может их найти.

Другие ответы показывают способы решения этой проблемы переносимым способом, по сути, помещая определения шаблонных функций-членов в место, которое видно из того места, где вы создаете экземпляры этого класса - либо посредством явного создания экземпляра, либо путем помещения реализации в файле, который #include из main.cpp.

Вы также можете прочитать документацию вашего компилятора, чтобы узнать, как они рекомендуют его настраивать. Я знаю, что компилятор IBM XLC имеет несколько различных настроек и опций для их настройки.

1 голос
/ 16 октября 2008

FAQ по C ++ охватывает это и несколько способов его обойти.

Вам не нужно делать все методы «встроенными», но вы должны определить тела методов в template_test.h, а не в template_test.cpp.

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

0 голосов
/ 16 октября 2008

Когда компилятор компилирует main.cpp, он видит, что определение класса имеет объявления функций-членов, но не имеет определений функций-членов. Он просто предполагает, что где-то должно быть определение конструктора «Templated» и реализации Foo, поэтому он обращается к компоновщику, чтобы найти его во время ссылки.

Решение вашей проблемы - поместить реализацию Templated в template.h.

например

template<class BaseClass>
class Templated : public BaseClass
    {
public:
    Templated(int a) : BaseClass(a) {}
    virtual int Foo() { return BaseClass::b; }
    };

Интересно, что я могу получить ваш код для ссылки, поместив его в конец template_test.cpp.

void Nobody_Ever_Calls_This()
{
    Templated<Base> dummy(1);
}

Теперь компилятор может найти экземпляр Templated для ссылки. Я бы не рекомендовал это как технику. Какой-то другой файл может захотеть создать

Templated<Widget>

и тогда вам нужно будет добавить еще одно явное создание экземпляра в template_test.cpp.

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