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

У меня есть класс Base в base.h, который имеет функцию шаблона

class Base {
 template <typename T> void test(T a);
}

этот шаблон должен читать в int или же double типа, и у меня есть класс Derived, который является производным от класса Base

Я пытался вызвать функцию test в классе Derived, но у меня ошибка компоновщика.

В конце концов, я понял, что если в base.cpp, Я добавить

void test(int a);
void test(double a);

не будет ошибки компилятора. Это решение кажется неудобным, есть ли лучшее решение? Спасибо

3 ответа

Решение

Шаблоны C++ должны быть определены (с полным телом функции) в том же модуле перевода (файл.CPP плюс все включенные файлы заголовков), где они используются. В вашем заголовочном файле все, что вы сделали, - объявили (учитывая имя и подпись) функцию. В результате, когда вы включаете base.hвсе, что видит компилятор:

class Base {
  template <typename T> void test(T a);
}

Это объявляет, но не определяет функцию. Чтобы определить его, вы должны включить тело функции:

class Base {
  template <typename T> void test(T a)
  {
    // do something cool with a here
  }
}

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

Base obj;
obj.test< int >( 1 );
obj.test< char >( 'c' );

Компилятор сгенерирует два набора машинного кода на основе Base::test шаблон, один для int и один для char, Ограничением здесь является то, что определение Base::test шаблон должен находиться в том же модуле перевода (файл.CPP), иначе компилятор не будет знать, как создавать машинный код для каждой версии Base::test функция. Компилятор работает только с одним модулем перевода за раз, поэтому он не знает, определены ли вы Base::test< T > в каком-то другом файле CPP. Он может работать только с тем, что имеет под рукой.

Это сильно отличается от того, как работают дженерики в C#, Java и аналогичных языках. Лично мне нравится думать о шаблонах как о текстовом макросе, который расширяется компилятором по мере необходимости. Это заставляет меня иметь в виду, что полное тело функции шаблона должно быть включено в любой файл CPP, где она используется.

Вы должны полностью определить функцию шаблона test прежде чем он может быть использован. Самый простой способ сделать это - просто написать тело функции в заголовке base.h:

class Base {
 template <typename T> void test(T a)
 {
    ... function body here
 }
}

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

Другие вопросы по тегам