Очень распространенный шаблон кодирования в C и C ++ для функций, ADT и классов должен иметь:
Они скомпилированы в отдельный объект (общий или нет). Другой код, использующий объявленную сущность (назовем ее ‘foo’), включает файл foo.h, компилируется отдельно и затем ссылается на foo.o.
Однако с шаблонным foo это невозможно: без указания того, для какого типа требуется создание экземпляра, foo.o бесполезен. Кажется, что все делают, включая код реализации (который обычно был бы foo.cpp) в том же модуле перевода, что и пользовательский код.
Я хотел бы быть в состоянии избежать этого, используя некоторый механизм, который не требует включения foo.cpp. В идеале это должно работать:
main.cpp:
#include "foo.h"int main() {
foo<int>();
}
foo.h:
template<typename T> void foo();
foo.cpp:
template<typename T> void foo() {
// implementation here
}
Для этого, я полагаю, мне нужна какая-то умная идиома, возможно, включающая мой механизм сборки, а не только исходные файлы и файлы заголовков, так что мне не нужно включать код реализации, только заголовок. Я думал, возможно, что-то вроде строк файла реализации, включая некоторые автоматически сгенерированный заголовок с такими строками, как
template foo<int>();
который будет анализировать объектные файлы для отсутствующих экземпляров (то есть два прохода компиляции) или, возможно, синтаксический анализ источника (настройка компилятора? включение некоторого вспомогательного вывода?); Вы можете предложить такую идиому? Или другую альтернативу я не рассматривал?
Замечания: Конечно, весь смысл в том, что код для модуля foo (foo.h
, foo.cpp
) не «знает», какие экземпляры нужны, например, не знает, main()
буду использовать foo<int>()
или же foo<unsigned char>()
,
Это явное воплощение.
template foo<int>();
Это говорит компилятору сделать foo
реализовано для int
, Как будто вы пишете реализацию для int
в вашем .cpp
файл. Это полезно, если вы знаете, какие типы будут использоваться в будущем. Это может сократить общее время компиляции в вашем проекте, если вы используете foo<int>
во многих переводческих подразделениях. Например:
файл FooBar.h
:
template <typename T>
class Bar
{
public:
T data;
void func();
};
template<typename T> void foo();
файл FooBar.cpp
:
template <typename T>
void Bar<T>::func()
{
}
template <typename T>
void foo() {
}
// explicit instantiation
template class Bar<int>;
template class Bar<std::string>;
template foo<int>();
template foo<float>();
Основная проблема в том, что ваш foo
а также Bar
как шаблонные вещи должны использоваться только с экземплярами. Например, foo
ограничено int
а также float
, а также Bar
ограничено int
а также std::string
,
На самом деле, только для примера, нам даже не нужно дублировать подпись:
template decltype(foo<int>) foo;
это не имеет большого значения в этом примере, но имеет значение, если ваша подпись длинная и утомительная.