2015-01-29 4 views
2

Рассмотрим следующий код:Неопределенная ссылка на функцию-член шаблона базового класса

myclass.h:

template <class T> 
struct S { 
    void f(); 
}; 

struct MyClass : public S<int> 
{ 
    void g(); 
}; 

myclass.cpp:

#include "myclass.h" 
#include <iostream> 

template <class T> 
void S<T>::f() 
{ 
    std::cout << "f()\n"; 
    /* some code here that could not be in header */ 
} 

void MyClass::g() 
{ 
    std::cout << "g()\n"; 
} 

main.cpp:

#include "myclass.h" 

int main() 
{ 
    MyClass m; 
    m.g(); 
    m.f(); // problem here 
} 

У меня есть линкер ror:

undefined reference to `S::f()'

Можно ли решить эту проблему без передачи реализации файла S :: f() в заголовочный файл? Почему S :: f() не создается при объявлении MyClass из полного специализированного базового класса шаблонов?

+1

Возможный дубликат [Почему шаблоны могут быть реализованы только в файле заголовка?] (Http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header- file) – molbdnilo

+0

Код, который не должен быть в заголовке, может ли он быть помещен в статическую или глобальную (не шаблонную) функцию? Или с использованием, например, [pimpl idiom] (http://en.wikipedia.org/wiki/Opaque_pointer)? –

ответ

1

Why S::f() not instantiated when I declare a MyClass derived from full specialized template base class?

Потому что ваш myclass.cpp не использует этот шаблон, как и все неиспользованные шаблоны в единицах перевода, они ни к чему в получаемом коде. Вывод все хорошо и хорошо, и если MyClass::g() использовал S<T>::f(), вы бы потянули этот код, и жизнь была бы хорошей. Но вы этого не сделаете, поэтому вам придется потянуть его по-другому ...

Вы должны иметь возможность сделать это с помощью явного экземпляра. Обратите внимание на следующее дополнение к вашему .cpp-файлу:

#include "myclass.h" 
#include <iostream> 

template <class T> 
void S<T>::f() 
{ 
    std::cout << "f()\n"; 
} 

void MyClass::g() 
{ 
    std::cout << "g()\n"; 
} 

template struct S<int>; // ADDED 

Будьте осторожны, однако. Это будет работать, только если используется S<int>. Дополнительные расширения потребуют дополнительных записей явных экземпляров.

Вы также можете сделать это с прямой специализации, такие как изменение вашего .cpp, чтобы просто сделать это:

#include "MyClass.h" 
#include <iostream> 

template <> 
void S<int>::f() 
{ 
    std::cout << "f()\n"; 
} 

void MyClass::g() 
{ 
    std::cout << "g()\n"; 
} 

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

В любом случае, удачи.

+0

Большое спасибо. Я попробовал явное создание экземпляра, но сделал это выше реализации f(). Когда я его перемещаю - код скомпилирован успешно. – alexolut

0

Вы не можете решить эту проблему, не передав реализацию функции s :: f() в заголовочный файл. это связано с тем, что компилятор должен знать реализацию шаблона перед его использованием.

1

Добавление явного экземпляра

template 
struct S<int>; 

к "myclass.cpp" делает код компиляции.

Вкратце, вывод из S<int> только «определяет» определение класса, а не членов.
Компилятор не может создать экземпляр f в этот момент, так как определение f неизвестно.

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

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