Почему реализация и объявление класса шаблона должны быть в одном заголовочном файле? Может ли кто-нибудь из вас объяснить это на примере?
Почему реализация и объявление класса шаблона должны быть в одном заголовочном файле?
Ответ 1
Компилятор должен иметь доступ ко всему определению шаблона (а не только к сигнатуре), чтобы генерировать код для каждого экземпляра шаблона, поэтому вам нужно переместить определения функций в свой заголовок.
Подробнее читайте Модель включения.
Ответ 2
Определение шаблона класса и реализация его функций-членов должно быть видимым для каждого места, которое создает экземпляр его с отдельным типом. т.е. для создания экземпляра myTemplate<int>
вам необходимо увидеть полное определение и реализацию myTemplate
.
Самый простой способ сделать это - поместить определение шаблона и его функций-членов в один заголовок, но есть и другие способы. Например, вы можете поместить реализацию функций-членов в отдельный файл, который был включен отдельно. Затем вы можете включить его из первого заголовка или включить только файл реализации, в котором он вам нужен.
Например, одна практика заключается в том, чтобы явно создать шаблон для отдельного набора параметров в одном .cpp файле и объявить те экземпляры extern
в заголовке. Таким образом, эти экземпляры могут использоваться в других исходных файлах, не требуя видимости функций-членов шаблона. Однако, если вы не включите файл реализации, вы не сможете использовать другие наборы параметров шаблона.
то есть. если у вас есть myTemplate<int>
и myTemplate<std::string>
, определенные как extern
, тогда вы можете использовать их в порядке, но если myTemplate<double>
не определен extern
, вы не сможете использовать это без реализации.
Ответ 3
В случае нормального класса объявления достаточно для компиляции, и соответствующие определения будут связаны.
В случае шаблонов компилятору также требуется определение для генерации кода.
Разница лучше объясняется в часто задаваемые вопросы по С++.
Ответ 4
Им не нужно.
Необходимо, чтобы определение шаблона было видимым в момент создания экземпляра (где оно использовалось), чтобы компилятор мог получить класс/функцию из шаблона в этой точке.
Однако чрезвычайно часто используется два файла заголовка для классов шаблонов:
// foo_fwd.hpp
template <typename T, typename U> struct Foo;
// foo.hpp
#include "foo_fwd.hpp"
template <typename T, typename U> struct Foo { typedef std::pair<T,U> type; };
Это позволяет тем, кому не требуется полное определение шаблона, включать несколько более легкий заголовок, например:
//is_foo.hpp
#include <boost/mpl/bool.hpp>
#include "foo_fwd.hpp"
template <typename Z>
struct is_foo: boost::mpl::false_ {};
template <typename T, typename U>
struct is_foo< Foo<T,U> >: boost::mpl::true_ {};
который может немного ускорить время компиляции.