Разделение определения / создания шаблонных классов без ‘extern’

Стандарт 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();
}

Теперь мне интересно:

  • Является ли наш подход допустимым кодом C ++ (03 или 11) или нам просто повезло, что он сработал?
  • С C ++ 11 мы сможем достичь того же, включив point.cpp в main.cpp и объявив extern template <int dim> struct Point;?

1

Решение

Ваш подход является допустимым кодом 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>;).

0

Другие решения

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 соответственно, более понятно, что файл содержит определение / реализацию определенного шаблона.

1

Является ли наш подход допустимым кодом 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.

1
По вопросам рекламы [email protected]