#c #templates #explicit-instantiation
#c #шаблоны #явное создание экземпляра
Вопрос:
У меня есть класс A с шаблонным аргументом T, который ограничен двумя типами: T1 и T2. Из-за этого я явно создал экземпляр класса A для типов T1 и T2, так что функциональность A может быть определена в исходном файле и ее не нужно перекомпилировать каждый раз, когда включен A.hpp.
A. ГЭС:
template<typename T>
class A {
public:
void basicMethod();
};
template class A<T1>;
template class A<T2>;
A.cpp:
template<typename T>
void A<T>::basicMethod() {
// ...
}
Однако теперь я хочу добавить шаблонный метод как для <T1>, так и для <T2>, где аргумент шаблона снова ограничен двумя типами: S1 и S2:
A. ГЭС:
template<typename T>
class A {
public:
void basicMethod();
template<typename S>
void advancedMethod();
};
template class A<T1>;
template class A<T2>;
// How to explicitly instantiate A::advancedMethod here?
A.cpp:
template<typename T>
void A<T>::basicMethod() {
// ...
}
template<typename T>
template<typename S>
void A<T>::advancedMethod() {
// ...
}
Как мне явно создать экземпляр <T>::advancedMethod<S> для (T, S) = {T1, T2} x {S1, S2}? Основываясь на том, что я нашел в Интернете, я попытался добавить подобные вещи в конец A.hpp:
template void A<T1>::advancedMethod(S1);
template void A<T1>::advancedMethod(S2);
template void A<T2>::advancedMethod(S1);
template void A<T2>::advancedMethod(S2);
Однако это вообще не сработало.
Комментарии:
1. Я предполагаю, что второй метод шаблона в вашем
.cpp
должен бытьadvancedMethod
.2. @super Да, извините, я только что исправил это.
Ответ №1:
Явные определения создания экземпляра для шаблонов функций-членов шаблонов классов
Вы почти поняли это. Правильный синтаксис:
template void A<T1>::advancedMethod<S1>();
template void A<T1>::advancedMethod<S2>();
template void A<T2>::advancedMethod<S1>();
template void A<T2>::advancedMethod<S2>();
В частности, следует отметить, что advancedMethod()
это шаблон функции, параметризованный по параметру шаблона одного типа, без параметров функции: ваши явные определения создания экземпляра для разной специализации шаблона функции (для разных специализаций шаблона класса, в котором он определен) должны, как и явные определения создания экземпляра для шаблона класса, предоставлять шаблонаргументы ( <...>
) для указания специализаций, которые вы хотите явно создать.
Наконец, шаблон advancedMethod()
функции, естественно, должен быть определен (либо через определение основного шаблона, либо в явной специализации) для каждой специализации, которая явно создается; в частности (cppreference):
Если шаблон функции, шаблон переменной, шаблон функции-члена или элемент функции-члена или элемент статических данных шаблона класса явно создается с явным определением экземпляра, определение шаблона должно присутствовать в той же единице преобразования.
Отделение определений шаблонных функций (функций-членов шаблона / class) от их объявлений: -timl.hpp
шаблон
В результате приведенного выше требования при предоставлении явных определений экземпляра вы обычно размещаете их после определений связанных шаблонных объектов в исходном файле, где типичным вариантом использования является то, что пользователь общедоступного API шаблонного объекта не должен иметь доступа к определениям (как в вашемпример). Это важно, поскольку вы не можете явно создавать экземпляр одной и той же специализации дважды для разных единиц перевода, согласно [temp.spec]/5:
Для заданного набора аргументов шаблона,
- явное определение экземпляра должно появляться в программе не более одного раза,
- […]
Для диагностики нарушения этого правила реализация не требуется.
Таким образом, если вы поместите явные экземпляры в заголовок, а затем включите заголовок в более чем один исходный файл, ваша программа будет неправильно сформирована, NDR.
// a.hpp
template<typename T>
class A {
public:
void basicMethod();
template<typename S>
void advancedMethod();
};
// a-timpl.hpp
#include "a.hpp"
template<typename T>
void A<T>::basicMethod() {
// ...
}
template<typename T>
template<typename S>
void A<T>::advancedMethod() {
// ...
}
// a.cpp
#include "a-timpl.hpp"
#include "t1.hpp"
#include "t2.hpp"
#include "s1.hpp"
#include "s2.hpp"
// Explicit instantiation definitions for
// production intent.
template class A<T1>;
template class A<T2>;
template void A<T1>::advancedMethod<S1>();
template void A<T1>::advancedMethod<S2>();
template void A<T2>::advancedMethod<S1>();
template void A<T2>::advancedMethod<S2>();
Для теста вы аналогичным образом включаете файл -timpl.hpp
заголовка, а не основной заголовок (основной заголовок предназначен для общедоступного API), так что вы можете использовать шаблонные определения для явного создания экземпляров специализаций, используемых в test:
// a_test.cpp
#include "a-timpl.hpp"
#include "t1_mock.h"
#include "t2_mock.h"
#include "s1_mock.h"
#include "s2_mock.h"
// Explicit instantiation definitions for
// test intent.
template class A<T1Mock>;
template class A<T2Mock>;
template void A<T1Mock>::advancedMethod<S1Mock>();
template void A<T1Mock>::advancedMethod<S2Mock>();
template void A<T2Mock>::advancedMethod<S1Mock>();
template void A<T2Mock>::advancedMethod<S2Mock>();
Комментарии:
1. Спасибо за ваш ответ. Ваш синтаксис имел гораздо больше смысла, я только что видел, как люди в некоторых других местах помещали параметры шаблона в круглые скобки и не совсем понимали, почему. Я пробовал ваш синтаксис раньше, и он не работал, однако оказалось, что это потому, что я поместил явные экземпляры после определения класса / объявления метода в A.hpp вместо после определения метода. Теперь, похоже, все компилируется и связывается, хотя я еще не тестировал вызов экземпляров метода шаблона.
2. Что касается второй части вашего ответа: я не совсем понимаю, в каких конкретных ситуациях это вызывает проблемы? Прямо сейчас я помещаю экземпляры классов сразу после определения класса (так в заголовочном файле), а экземпляры методов сразу после их определений (так в исходном файле). Когда я перемещаю экземпляры класса в конец одного из исходных файлов этого класса, я получаю ошибки компоновщика.
3. @Wout12345 Согласно [temp.spec]/5 , для данного набора аргументов шаблона «явное определение экземпляра должно появляться не более одного раза в программе […] Для диагностики нарушения этого правила реализация не требуется.» . Если вы поместите явные экземпляры в заголовок, а затем включите заголовок более чем в один исходный файл, ваша программа будет неправильно сформирована, NDR (диагностика не требуется). …
4. @Wout12345 … Если вы получаете ошибки компоновщика, вы делаете что-то неправильно: возможно, вы включили
a.hpp
файл вместоa-timpl.hpp
файла в исходный файл?5. Рассматриваемый заголовок защищен от включения, поэтому он может появиться только один раз во время моего процесса компиляции, а заголовок является внутренним (не является частью общедоступного интерфейса), поэтому он не будет частью каких-либо других процессов компиляции. Означает ли это, что он никогда не дублируется?