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

У меня есть класс 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);

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


person Michael    schedule 29.09.2011    source источник
comment
Опубликуйте больше кода. Как вы это называете? а какие ошибки?   -  person Nawaz    schedule 29.09.2011
comment
У меня не было проблем. Объявлена ​​ли функция шаблона и в вашем заголовке?   -  person sje397    schedule 29.09.2011
comment
В общем, чтение сообщений об ошибках компилятора может дать полезную информацию о том, в чем заключается проблема.   -  person Hugh    schedule 29.09.2011
comment
@ sje397 моя функция объявлена ​​в cpp, должна быть объявлена ​​в заголовке, спасибо, моя проблема решена. знак равно   -  person Michael    schedule 29.09.2011


Ответы (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, где она используется.

person Chris Vig    schedule 29.09.2011
comment
Честно говоря, их не нужно определять в заголовке всегда, а только для того, чтобы работало неявное создание экземпляров. OP уже понял, что это может быть в одной единице перевода, но это заставляет пользователя вручную создавать экземпляры шаблонов template void Base::test<int>(); - обычно не стоит заморачиваться. - person David Rodríguez - dribeas; 29.09.2011

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

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

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

person sukumar    schedule 19.10.2012