Вопрос о шаблонах и заголовках - PullRequest
2 голосов
/ 07 октября 2009

Компилятор говорит, что не может найти ссылку на функцию, когда я делаю это:

// link.h
template <class T>
    T *Link(T *&, T *(*)())

// link.cpp
template <class T>
T c:Link(T *&ChildNodeReference, T *(*ObjectCreator)()){

}

Если я реализую класс внутри заголовка, все пройдет гладко.

Пожалуйста, я буду работать над заголовком, пока кто-нибудь не просветит меня об этом.

В C ++ есть что-то странно раздражающее. Я знаю, есть причина для этого и т. Д. Несмотря на это, компиляторы не могут помочь вам в этом -_- "

Ответы [ 5 ]

6 голосов
/ 07 октября 2009

Шаблоны - это, по сути, полутип-безопасные макросы, отсюда и ограничение.

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

При использовании шаблонов компилятор C ++ должен компилировать каждое создание функции отдельно. Очевидно, что он не может сделать это «заранее», потому что набор типов, для которых вы можете создать экземпляр функции, фактически неограничен (вы всегда можете определить новый тип в вашем коде перед вызовом функции). Фактически, два экземпляра одного и того же шаблона функции могут быть совершенно разными. Рассмотрим этот крайний случай:

struct t1 {
  template <int>
  struct a {};
};

struct t2 {
  enum { a = 123 }; 
};

enum { b = 456, c = 789 };

template <class T>
void foo() {
   T::a<b>c;
}

Теперь, если мы вызываем foo<t1>(), оператор внутри него является объявлением локальной переменной, потому что t1::a является шаблоном класса:

T::a<b> c;

Но если мы вызываем foo<t2>(), оператор внутри является выражением, потому что t2::a является интегральной константой:

(T::a < b) > c;

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

Теперь, все это говорит о том, что ISO C ++ действительно позволяет разделить объявление и определение шаблонов, чтобы их можно было рассматривать как обычные функции с объявлениями в файлах .h и определениями в файлах .cpp. Это называется «шаблоны экспорта», потому что вы должны предшествовать как объявлению, так и определению с ключевым словом export:

// link.h
export template <class T>
T *Link(T *&, T *(*)());

// link.cpp
export template <class T>
T *Link(T *&ChildNodeReference, T *(*ObjectCreator)()) {
}

Это, однако, противоречивая особенность Стандарта из-за очень высокой нагрузки на реализацию, и большинство популярных реализаций отказываются от ее реализации; в частности, g ++, MSVC и C ++ Builder не реализуют его. Единственный известный мне компилятор, который поддерживает это Comeau C ++ .

5 голосов
/ 07 октября 2009

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

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

Когда компилятор находит экземпляр специализации шаблона (скажем, List<int>), он возвращается к включенному коду шаблона и компилирует его с этой специализацией, поэтому у вас нет проблем с переопределением кода функции.

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

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

4 голосов
/ 07 октября 2009

Шаблонные реализации (не только определения) должны быть доступны во время компиляции.

Итак, полный код шаблона обычно помещается в заголовочный файл.

0 голосов
/ 07 октября 2009

Вы забыли * для типа возврата. Таким образом, реализация не соответствует определению. Добавьте его, и оно должно работать:

T *c:Link(T *&ChildNodeReference, T *(*ObjectCreator)())
{
}

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

0 голосов
/ 07 октября 2009

Код шаблона должен быть в шапке. Извините, я полностью пропустил это! (и я думал, что годами кодирую C ++: P)

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