Как правильно сделать явную реализацию шаблона? - PullRequest
4 голосов
/ 01 февраля 2012

Я использую шаблоны для реализации шаблона CRTP.С приведенным ниже кодом я получаю ошибки компоновщика (для всех методов, определенных в базовом классе CPConnectionBase), например:

ошибка LNK2001: неразрешенный внешний символ "public: void __thiscall CPConnectionBase:: start (void) "(? start @? $ CPConnectionBase @ VTNCPConnection @@@@ QAEXXZ)

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

#include "TNCPConnection.h"
template class CPConnectionBase<TNCPConnection>;

в файл CPConnectionBase.cpp.Это, безусловно, неправильное место, поскольку я не хочу включать заголовок всех возможных производных классов в источник базового класса (я мог бы использовать базовый класс также в другом проекте с другими производными классами).

Поэтому моя цель - создать экземпляр шаблона в исходном файле производного класса (TNCPConnection.h или TNCPConnection.cpp), но я не смог найти решение.Добавление

template class CPConnectionBase<TNCPConnection>;

в файл TNCPConnection.cpp не решает мои проблемы с компоновщиком, а добавление

template<> class CPConnectionBase<TNCPConnection>;

в файл TNCPConnection.cpp дает мне ошибку времени компиляции:

ошибка C2908: явная специализация;'CPConnectionBase' уже был создан

Как можно избавиться от ошибок компоновщика, не делая реализацию базового класса зависимой от заголовочных файлов производных классов?

Здесьскелет моего кода:

CPConnectionBase.h

template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
    void start();
};

CPConnectionBase.cpp

#include "stdafx.h"
#include "CPConnectionBase.h"

template<class Derived>
void CPConnectionBase<Derived>::start()
{
    ...
}

TNCPConnection.h

#include "CPConnectionBase.h"

class TNCPConnection : public CPConnectionBase<TNCPConnection>
{
public:
    void foo(void);
};

TNCPConnection.cpp

#include "stdafx.h"
#include "TNCPConnection.h"

void TNCPConnection::foo(void)
{
    ...
}

Ответы [ 5 ]

3 голосов
/ 01 февраля 2012

Определение CPConnectionBase::start() должно быть доступно в том месте, где вы явно создаете экземпляр класса - в противном случае функция не будет создана, и это не создание экземпляра происходит без вывода сообщений (с последующими ошибками компоновщика).

Стандартным решением является заголовок CPConnectionBase.hpp, который определяет функции шаблона, объявленные в CPConnectionBase.h. Включите CPConnectionBase.hpp в TNCPConnection.cpp и явно создайте его экземпляр.

2 голосов
/ 18 октября 2012

Я должен добавить примечание: MSVC позволяет объявить явную специализацию перед функциями-членами внутри того же модуля компиляции.GCC (4.7) требует, чтобы они были в конце файла.Т.е.

MSVC:

template class TClass<Base>;

template <class T> void TClass<T>::Function() {}

GCC:

template <class T> void TClass<T>::Function() {}

template class TClass<Base>;

1 голос
/ 01 февраля 2012

Во-первых, я бы предложил переименовать CPConnectionBase.cpp в CPConnectionBase.inl, так как он не содержит никакого не шаблонного кода.

В момент создания экземпляра шаблона вам необходимо #include <CPConnectionBase.inl> первый.Я бы предложил сделать это в TNCPConnection.cpp.

В качестве альтернативы, вы можете переместить реализацию CPConnectionBase в файл заголовка CPConnectionBase.h, и компилятор автоматически обработает реализацию.

0 голосов
/ 01 февраля 2012

Вы также можете использовать «Модель разделения» . Просто определите шаблон в одном файле и отметьте определение с ключевым словом export :

export template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
  public:
   void start();
};
0 голосов
/ 01 февраля 2012

перемещает шаблонный код из .cpp в заголовок.

Когда вы включаете заголовок с шаблонами, шаблонный код генерируется в целевом коде в соответствии с тем, что он находит в заголовке.

Если код находится в файле .cpp, он не может быть найден и, следовательно, не может быть сгенерирован (поскольку вы включили только .h)

...