#c #templates #typechecking
#c #шаблоны #проверка типов
Вопрос:
Допустим, у меня есть функция с шаблонным типом T и двумя другими классами A и B.
template <typename T>
void func(const T amp; t)
{
...........
//check if T == A do something
...........
//check if T == B do some other thing
}
Как я могу выполнить эти две проверки (без использования библиотеки Boost)?
Ответ №1:
Если вы буквально просто хотите, чтобы логическое значение проверяло T == A
, тогда вы можете использовать is_same
, доступный в C 11 как std::is_same
, или до этого в TR1 как std::tr1::is_same
:
const bool T_is_A = std::is_same<T, A>::value;
Вы можете тривиально написать этот маленький класс самостоятельно:
template <typename, typename> struct is_same { static const bool value = false;};
template <typename T> struct is_same<T,T> { static const bool value = true;};
Часто, однако, вам может показаться более удобным упаковать ваш код ветвления в отдельные классы или функции, для которых вы специализируетесь A
, и B
, поскольку это даст вам условие времени компиляции. Напротив, проверка if (T_is_A)
может быть выполнена только во время выполнения.
Комментарии:
1. Что вы имеете в виду, только во время выполнения?
is_same<T,A>::value
является целочисленным постоянным выражением. Вводимый код,if(T_is_A)
вероятно, подлежит удалению мертвого кода в приличном компиляторе, что происходит перед выполнением. Проблема в том, что код в обеих ветвях все еще должен компилироваться, прежде чем он будет удален.2. @SteveJessop: Ну, эта оптимизация — это деталь … главное, как вы говорите, в том, что весь код ветви должен быть допустимым кодом, что является серьезным ограничением. Напротив, специализированные шаблоны не обязаны иметь смысл, если они на самом деле не созданы.
3. Это решение имеет преимущество перед уже предлагаемыми, потому что, если «do something» и «do some other thing» определяют переменную как double и определяют переменную как int, технология специализации функций не будет работать, поскольку эти переменные будут локальными для этих функций. Спасибо за хорошее решение!
Ответ №2:
Создавайте шаблоны функций со специализациями, которые будут делать то, что вы хотите.
template <class T>
void doSomething() {}
template <>
void doSomething<A>() { /* actual code */ }
template <class T>
void doSomeOtherThing() {}
template <>
void doSomeOtherThing<B>() { /* actual code */ }
template <typename T>
void func(const T amp; t)
{
...........
//check if T == A do something
doSomething<T>();
...........
//check if T == B do some other thing
doSomeOtherThing<T>();
}
Комментарии:
1. Если вы вообще опустите определение основного шаблона,
template <class> void doSomething();
, то вы получите приятную ошибку во время компиляции, если тип не является ниA
norB
.2. @KerrekSB: На самом деле, в этом случае вы получите ошибку времени компиляции независимо от
T
того, потому что специализация не сможет найти свой основной шаблон. Вот почему оба включены 🙂3. Действительно, вам пришлось бы опустить общее определение и добавить определение «ничего не делать» для
doSomething<B>
anddoSomeOtherThing<A>
.4. Да, я неправильно истолковал вопрос OP как «включение»
T
, но на самом деле это не то, о чем просили. Не бери в голову:-S5. @SteveJessop: Да, но семантика будет немного отличаться. Для этого потребуется фактический
T
аргумент as (не проблема, OP, вероятно, все равно захочет его использовать), и он будет специализироваться наT
s, которые неA
являются, но конвертируются вT
(илиTamp;
, зависит от того, как вы это делаете).
Ответ №3:
Если вы хотите иметь специальную реализацию func
для некоторого типа параметра, просто создайте перегрузку, специфичную для этого типа:
template <typename T>
void func(const T amp; t) {
// generic code
}
void func(const A amp; t) {
// code for A
}