Стандарт C ++ 11 (уже не такой новый) представил extern
Ключевое слово для шаблонов. Его цель состоит в том, чтобы сообщить компилятору, что шаблон не должен быть создан в точке использования, но что он будет создан в другом модуле перевода (и, таким образом, экземпляр будет доступен во время ссылки) — по крайней мере, AFAIK.
Теперь, даже в эпоху до C ++ 11 мы использовали нечто подобное, чтобы отделить объявление / определение шаблонных классов от его реализации для ускорения компиляции, например, вот так:
point.h: определение класса
template <int dim> struct Point {
...
void foo();
...
};
point.cpp: определения методов
#include "point.h"template <int dim>
void Point<dim>::foo() {
...
}
point_2d.cpp: создание класса (2D версия)
#include "point.cpp"template struct Point<2>;
point_3d.cpp: создание класса (3D версия)
#include "point.cpp"template struct Point<3>;
main.cpp: использование 2D и 3D очков
#include "point.h"int main(int, char**) {
Point<2> p;
p.foo();
}
Теперь мне интересно:
point.cpp
в main.cpp
и объявив extern template <int dim> struct Point;
?Ваш подход является допустимым кодом C ++ и должен работать как на C ++ 03, так и на C ++ 11. Это называется явной реализацией:
template struct Point<2>;
При применении к шаблонам классов он явно создает экземпляры всех членов шаблона класса для некоторого списка аргументов шаблона в текущем модуле перевода. При таком подходе просто создать библиотеки шаблонов, для которых набор возможных аргументов шаблона известен заранее (как в вашем случае, когда Point
может быть 2D и 3D, но не 1D, 4D и т. д.).
В C ++ 11, когда вы добавляете в явную директиву ключевое слово extern
:
extern template struct Point<2>;
это становится декларацией, а не определением. Поведение такой вещи похоже на обычное extern
Ключевое слово для переменных. Явное объявление создания шаблона может использоваться вместе с явным созданием следующим образом:
// point.h
template <int dim> struct Point {
...
void foo();
...
};
extern template struct Point<2>;
extern template struct Point<3>;
#include "point.hpp"
// point.hpp
#include "point.h"
template <int dim>
void Point<dim>::foo() {
...
}
// point.cpp
#include "point.hpp"
template struct Point<2>;
template struct Point<3>;
С таким кодом вы получаете тот же результат, что и с вашим, но дополнительно вы разрешаете пользователям вашего кода использовать Point<1>
, Point<4>
и другие специализации Point
Шаблон класса, если они хотят. Без extern template struct Point<2>;
а также extern template struct Point<3>;
директивы Point
шаблон класса будет неявно создан в коде пользователя даже для аргументов шаблона 2
а также 3
что уменьшает смысл явной реализации (template struct Point<2>;
а также template struct Point<2>;
).
C ++ 11 использует extern
сказать компилятору не для создать экземпляр шаблона, а синтаксис использует конкретный тип, в отличие от предложенного синтаксиса в вопросе:
extern template struct Point<2>;
В C ++ 03 компилятор должен создавать экземпляр шаблона всякий раз, когда он наблюдает Point<2>
в модуле перевода и в C ++ 11 он знает, что не должен, когда он связан с ключевым словом extern.
На ваш вопрос, что вы сделали в C ++ 03, это разделили определение шаблона в отдельный заголовочный файл (с cpp
суффикс (см. ниже), и этот подход все еще будет работать с C ++ 11:
#include "point.ipp"
extern template struct Point<2>; // instantiated elsewhere
int main(int, char**) {
Point<2> p;
p.foo();
}
Субъективно мне тоже не нравится cpp
суффиксов для заголовочных файлов шаблонов так много, что я хотел обратить на это внимание. Это сбивает с толку и вводит в заблуждение, особенно когда кто-то видит файл cpp, включенный в другой файл cpp.
Рассмотрите возможность использования ipp
или же ixx
как расширение файла в сочетании с hpp
а также hxx
соответственно, более понятно, что файл содержит определение / реализацию определенного шаблона.
Является ли наш подход допустимым кодом C ++ (03 или 11) или нам просто повезло, что он сработал?
Ваше использование допустимо, но не канонично. Более распространенный стиль C ++ 98 будет группировать явные определения экземпляров вместе с реализацией в point.cpp
файл.
// point.h
template <int dim> struct Point {
...
void foo();
...
};
// point.cpp
#include "point.h"
template <int dim>
void Point<dim>::foo() {
...
}
// Now the implementation is available, so this is the time to use it:
template struct Point<2>;
template struct Point<3>;
С C ++ 11 мы сможем достичь того же, включив
point.cpp
вmain.cpp
и объявивextern template <int dim> struct Point;
?
Ты никогда не должен #include
.cpp
файл. Канонический стиль — поместить реализацию шаблона функции в файл заголовка, либо непосредственно в point.h
или в файле реализации шаблона с именем, например, point.hpp
,
Преимущество, добавленное C ++ 11, заключается в том, что вы можете сократить время компиляции, а также использовать Point<4>
по неявной специализации. Тем не менее, поместив реализацию в заголовок с extern
специализация не ускорит компиляцию Point<2>
а также Point<3>
,
Вы не можете буквально сказать extern template <int dim> struct Point;
, В заголовке должен быть указан список всех специализаций:
extern template struct Point<2>;
extern template struct Point<3>;
делегирование все специализация где-то еще была намеченной целью export
шаблоны. Это была особенность C ++ 98, которая оказалась слишком сложной для реализации, поэтому она была удалена из стандарта C ++ 11.