Метапрограммирование на C++ — различия между версиями
м |
|||
| Строка 43: | Строка 43: | ||
Мы можем добавить к сигнатуре этих функций формальный параметр и перенести | Мы можем добавить к сигнатуре этих функций формальный параметр и перенести | ||
| − | информацию о типе итератора, с которым работает эта функция из ее имени в этот | + | информацию о типе итератора, с которым работает эта функция, из ее имени в этот |
параметр: | параметр: | ||
| Строка 67: | Строка 67: | ||
== Замена числовых идентификаторов на типы == | == Замена числовых идентификаторов на типы == | ||
| − | С помощью следующего трюка можно переписать | + | С помощью следующего трюка можно переписать функцию, поведение которой |
зависит от числового идентификатора, так, чтобы ее поведение зависело от | зависит от числового идентификатора, так, чтобы ее поведение зависело от | ||
формального параметра (как в предыдущем разделе): | формального параметра (как в предыдущем разделе): | ||
| Строка 90: | Строка 90: | ||
== Substitution Failure Is Not An Error (SWINAE) == | == Substitution Failure Is Not An Error (SWINAE) == | ||
| + | При создании экземпляров шаблонных функций могут возникать компиляции. | ||
| + | Рассмотрим следующий код: | ||
| + | |||
| + | <source lang="cpp"> | ||
| + | int diff(int a, int b) { | ||
| + | return a - b; | ||
| + | } | ||
| + | |||
| + | template<typename T> | ||
| + | typename T::diff_type diff(T a, T b) { | ||
| + | return a - b; | ||
| + | } | ||
| + | </source> | ||
| + | |||
| + | При вызове <code>diff(3, 4)</code> компилятор попытается создать экземпляр | ||
| + | функции <code>diff<int>(int, int)</code>, но это приведет к ошибке компиляции, | ||
| + | поскольку <code>int::diff_type</code> не определено. Но эта ошибка не приводит | ||
| + | к выдаче сообщения об ошибке и прекращению компиляции, поскольку есть нешаблонная | ||
| + | функция, которую можно вызвать. | ||
== Проверка наличия метода у класса == | == Проверка наличия метода у класса == | ||
== Списки типов == | == Списки типов == | ||
Версия 21:53, 7 мая 2011
Мы рассмотрим несколько техник использования шаблонов C++.
Содержание
Статический assert
В следующем примере приведен код, который компилируется только на 64-разрядной платформе:
char[sizeof(int*)] == 8 ? 1 : -1]
Если код компилируется не на 64-разрядной платформе, то
sizeof(int*) != 8, что приведет к объявлению массива a отрицательного
размера, а это запрещено стандартом.
Эта идея используется в макросе BOOST_STATIC_ASSERT, предоставляемом
модулем Static Assert библиотеки Boost.
Tag passing
Предположим, нам нужно написать функцию, которая циклически переставляет элементы массива:
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>);
Substitution Failure Is Not An Error (SWINAE)
При создании экземпляров шаблонных функций могут возникать компиляции. Рассмотрим следующий код:
int diff(int a, int b) {
return a - b;
}
template<typename T>
typename T::diff_type diff(T a, T b) {
return a - b;
}
При вызове diff(3, 4) компилятор попытается создать экземпляр
функции diff<int>(int, int), но это приведет к ошибке компиляции,
поскольку int::diff_type не определено. Но эта ошибка не приводит
к выдаче сообщения об ошибке и прекращению компиляции, поскольку есть нешаблонная
функция, которую можно вызвать.