LINUX.ORG.RU

Undefined reference при использовании шаблона

 , ,


0

2

Доброго времени суток! Имеется такая вот загвоздка. Есть такой код на C++:

A.h

#ifndef FOO_H
#define FOO_H

template<typename T>
void foo(T);

#endif // FOO_H

A.cpp

#include "foo.h"

template<typename T>
void foo(T val)
{
    int a=5;
}

main.cpp

#include "foo.h"

int main()
{
    foo(5);
    return 0;
}

При компиляции сего кода я получаю в Qt Creator такую ошибку: undefined reference to `void foo<int>(int)'

При этом если при этом я удалю все шаблоны и заменю их допустим на int, то всё ок компилится и работает. И если я весь код определения перенесу из A.cpp в A.h, то всё тоже компилится и работает.

ЧЯДНТ? Подскажите, а то я не сталкивался с таким никогда. Даже не догадываюсь, куда копать. Кто в гуру в крестах? И почему такой код недопустим?

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

anatoly
()
Ответ на: комментарий от zamazan4ik
#ifndef FOO_H
#define FOO_H

template<typename T>
void foo(T val)
{
    int a=5;
}

#endif // FOO_H

И без A.cpp

Причина: компилятор компилирует все cpp файлы. Для него символ функции это закодированный класс, неймспейс, темплейт... После компиляции main.cpp захочет функцию что-то вроде foo_int (как она будет правильно называться можно посмотреть с помощью int), но линкеровщик эту функцию не находит.

ymuv ★★★★
()
Последнее исправление: ymuv (всего исправлений: 2)
Ответ на: комментарий от zamazan4ik

И почему такой код недопустим?

Потому что компилятор, компилируя A.cpp, не знает с каким T ты будешь использовать template<typename T> void foo(T val). Соответственно, он не может создать инстанс шаблона в этой единице трансляции. Далее в другой единице трансляции - main.cpp - ты пытаешься использовать специализацию этой функции для int. Компилятору достаточно декларации, он генерирует код, в котором используется ссылка на внешнее определение, но при линковке всего вместе нужное определение не находится, потому что не инстанцировался шаблон для T = int.

Когда определение шаблона находится в заголовочнике, шаблон инстанцируется в текущей единице трансляции (если ты в C++11 это явно компилятору не запретишь). Так обычно шаблоны и используются.

asaw ★★★★★
()
Ответ на: комментарий от zamazan4ik

Компилятор все cpp-файлы компилирует независимо друг от друга. Из шаблона код генерируется только при вызове. А поскольку вызов foo<int> происходит в main.cpp, то объектный файл, генерируемый из A.cpp, не содержит кода для foo<int>.

anatoly
()
Ответ на: комментарий от ymuv

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

ymuv ★★★★
()
Ответ на: комментарий от anatoly

Из шаблона код генерируется только при вызове.

В принципе, можно попросить компилятор инстанцировать foo для int в A.cpp.

asaw ★★★★★
()
Ответ на: комментарий от ymuv

Что-то вроде:

#include "A.h"
void dummyFunc() {
    foo(1);
    foo(1.1);
}

template<typename T>
void foo(T val)
{
    int a=5;
}

Тогда можно вызвать с int и double.

ymuv ★★★★
()
Ответ на: комментарий от asaw

Есть какие-нибудь красивые способы решения данной проблемы? Кроме как такого вот явного инстанцирования? Как принято решать такие проблемы? Описание всего с шаблонами писать сразу в хедере? или всё же надо просить явно компил инстанцировать?

zamazan4ik ★★
() автор топика
Ответ на: комментарий от asaw

Ну вообще если хедер очень жирный - можно еще использовать precompiled headers.

anonymous
()
Ответ на: комментарий от zamazan4ik

Есть какие-нибудь красивые способы решения данной проблемы?

Раньше это можно было сделать с помощью export, но это поддерживали полтора компилятора, и в C++11 export выпилили. Теперь, пока в C++ не появятся полноценные модули, даже не расчитывай.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.