Метапрограммирование на C++ — различия между версиями
(Новая страница: «TODO») |
|||
| Строка 1: | Строка 1: | ||
| − | + | Мы рассмотрим несколько техник использования шаблонов C++. | |
| + | |||
| + | == Статический assert == | ||
| + | В следующем примере приведен код, который компилируется только | ||
| + | на 64-разрядной платформе: | ||
| + | |||
| + | <source lang="cpp"> | ||
| + | char[sizeof(int*)] == 8 ? 1 : -1] | ||
| + | <source> | ||
| + | |||
| + | Если код компилируется не на 64-разрядной платформе, то | ||
| + | sizeof(int*) != 8, что приведет к объявлению массива a отрицательного | ||
| + | размера, а это запрещено стандартом. | ||
| + | |||
| + | Эта идея используется в макросе BOOST_STATIC_ASSERT, предоставляемом | ||
| + | модулем Static Assert библиотеки Boost. | ||
| + | |||
| + | == Tag passing == | ||
| + | |||
| + | Предположим, нам нужно написать функцию, которая циклически | ||
| + | переставляет элементы массива: | ||
| + | |||
| + | <source lang="cpp"> | ||
| + | template<typename It> | ||
| + | void rotate(It p, It, m, It q); | ||
| + | </source> | ||
| + | |||
| + | где p, q --- итераторы, указывающие на начало и конец массива, а элемент, | ||
| + | на который указывает m, после завершения работы функции будет располагаться | ||
| + | на месте элемента, на который указывает p. | ||
| + | |||
| + | Допустим, у нас есть разные реализации этой функции для разных типов итераторов: | ||
| + | |||
| + | <source lang="cpp"> | ||
| + | template<typename It> | ||
| + | void rotate_bidirectional(It p, It, m, It q); | ||
| + | |||
| + | ... | ||
| + | |||
| + | template<typename It> | ||
| + | void rotate_random_access(It p, It, m, It q); | ||
| + | </source> | ||
| + | |||
| + | Мы можем добавить к сигнатуре этих функций формальный параметр и перенести | ||
| + | информацию о типе итератора, с которым работает эта функция из ее имени в этот | ||
| + | параметр: | ||
| + | |||
| + | <source lang="cpp"> | ||
| + | template<typename It> | ||
| + | void rotate(It p, It, m, It q, bidirectional_tag); | ||
| + | |||
| + | ... | ||
| + | |||
| + | template<typename It> | ||
| + | void rotate(It p, It, m, It q, random_access_tag); | ||
| + | </source> | ||
| + | |||
| + | Тогда исходную функцию можно реализовать так: | ||
| + | |||
| + | <source lang="cpp"> | ||
| + | template<typename It> | ||
| + | void rotate(It p, It, m, It q) { | ||
| + | rotate(p, m, q, iterator_traits<It>::iterator_category()); | ||
| + | } | ||
| + | </source> | ||
| + | |||
| + | == Замена числовых идентификаторов на типы == | ||
| + | |||
| + | С помощью следующего трюка можно переписать функции, поведение которой | ||
| + | зависит от числового идентификатора, на так, чтобы ее поведение зависело от | ||
| + | формального параметра (как в предыдущем разделе): | ||
| + | |||
| + | <source lang="cpp"> | ||
| + | void foo(int); | ||
| + | </source> | ||
| + | |||
| + | Мы можем определить шаблонную структуру: | ||
| + | <source lang="cpp"> | ||
| + | template<int i> | ||
| + | struct int2type { | ||
| + | static const int value = i; | ||
| + | } | ||
| + | </source> | ||
| + | |||
| + | Теперь функцию foo можно переписать так: | ||
| + | <source lang="cpp"> | ||
| + | template<int i> | ||
| + | void foo(int2type<i>); | ||
| + | </source> | ||
| + | |||
| + | == Substitution Failure Is Not An Error (SWINAE) == | ||
| + | |||
| + | == Проверка наличия метода у класса == | ||
| + | |||
| + | == Списки типов == | ||
Версия 16:31, 7 мая 2011
Мы рассмотрим несколько техник использования шаблонов C++.
Содержание
Статический assert
В следующем примере приведен код, который компилируется только на 64-разрядной платформе:
char[sizeof(int*)] == 8 ? 1 : -1]
<source>
Если код компилируется не на 64-разрядной платформе, то
sizeof(int*) != 8, что приведет к объявлению массива a отрицательного
размера, а это запрещено стандартом.
Эта идея используется в макросе BOOST_STATIC_ASSERT, предоставляемом
модулем Static Assert библиотеки Boost.
== Tag passing ==
Предположим, нам нужно написать функцию, которая циклически
переставляет элементы массива:
<source lang="cpp">
template<typename It>
void rotate(It p, It, m, It q);
где p, q --- итераторы, указывающие на начало и конец массива, а элемент, на который указывает m, после завершения работы функции будет располагаться на месте элемента, на который указывает p.
Допустим, у нас есть разные реализации этой функции для разных типов итераторов:
template<typename It>
void rotate_bidirectional(It p, It, m, It q);
...
template<typename It>
void rotate_random_access(It p, It, m, It q);
Мы можем добавить к сигнатуре этих функций формальный параметр и перенести информацию о типе итератора, с которым работает эта функция из ее имени в этот параметр:
template<typename It>
void rotate(It p, It, m, It q, bidirectional_tag);
...
template<typename It>
void rotate(It p, It, m, It q, random_access_tag);
Тогда исходную функцию можно реализовать так:
template<typename It>
void rotate(It p, It, m, It q) {
rotate(p, m, q, iterator_traits<It>::iterator_category());
}
Замена числовых идентификаторов на типы
С помощью следующего трюка можно переписать функции, поведение которой зависит от числового идентификатора, на так, чтобы ее поведение зависело от формального параметра (как в предыдущем разделе):
void foo(int);
Мы можем определить шаблонную структуру:
template<int i>
struct int2type {
static const int value = i;
}
Теперь функцию foo можно переписать так:
template<int i>
void foo(int2type<i>);