Метапрограммирование на C++ — различия между версиями
| Строка 33: | Строка 33: | ||
<source lang="cpp"> | <source lang="cpp"> | ||
| − | template<typename It> | + | '''Полужирное начертание'''template<typename It> |
void rotate_bidirectional(It p, It, m, It q); | void rotate_bidirectional(It p, It, m, It q); | ||
| Строка 89: | Строка 89: | ||
</source> | </source> | ||
| − | == Substitution Failure Is Not An Error ( | + | == Substitution Failure Is Not An Error (SFINAE) == |
При создании экземпляров шаблонных функций могут возникать компиляции. | При создании экземпляров шаблонных функций могут возникать компиляции. | ||
Рассмотрим следующий код: | Рассмотрим следующий код: | ||
| Строка 111: | Строка 111: | ||
== Проверка наличия метода у класса == | == Проверка наличия метода у класса == | ||
| + | |||
| + | В следующем примере показано, как с помощью шаблонов можно проверить | ||
| + | наличие метода <code>size</code> у класса. | ||
<source lang="cpp"> | <source lang="cpp"> | ||
| Строка 147: | Строка 150: | ||
} | } | ||
</source> | </source> | ||
| + | |||
| + | При создании экземпляра класса <code>check_size<std::vector<int></code> создается | ||
| + | первый вариант функции <code>check_size</code>, поскольку можно без проблем создать | ||
| + | экземпляр структуры <code>wrap</code>. | ||
| + | |||
| + | При создании экземпляра класса <code>check_size<bar></code> создается второй вариант | ||
| + | функции <code>check_size</code>, поскольку создание экземпляра структуры <code>wrap</code> | ||
| + | приводит к ошибке. | ||
| + | |||
| + | ''Замечание'' Я не совсем понял, почему нельзя первый вариант функции <code>check_size</code> | ||
| + | написать так: | ||
| + | <source lang="cpp"> | ||
| + | template<typename T> | ||
| + | true_type check(T*, size_t (T::*)() const = &T::size) { } | ||
| + | </source> | ||
| + | |||
| + | При создании экземпляра класса <code>check_size<bar></code> компилятор говорит, что | ||
| + | у класса <code>bar</code> нет метода <code>size</code>. В этом случае SWIFAE не работает? | ||
== Списки типов == | == Списки типов == | ||
| + | На шаблонах можно реализовать списки в функциональном стиле. | ||
Версия 03:27, 8 мая 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 (SFINAE)
При создании экземпляров шаблонных функций могут возникать компиляции. Рассмотрим следующий код:
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 не определено. Но эта ошибка не приводит
к выдаче сообщения об ошибке и прекращению компиляции, поскольку есть нешаблонная
функция c подходящей сигнатурой.
Проверка наличия метода у класса
В следующем примере показано, как с помощью шаблонов можно проверить
наличие метода size у класса.
#include <iostream>
#include <vector>
typedef char true_type;
class false_type { true_type a[2]; };
template<typename T, size_t (T::*)() const>
struct wrap { };
template<typename T>
true_type check(T*, wrap<T, &T::size> = wrap<T, &T::size>()) { }
template<typename T>
false_type check(void*) { }
template<typename T>
struct check_size {
static const bool value = sizeof(check<T>((T*)0)) == sizeof(true_type);
};
class bar {
void no_size() { }
};
int main() {
if (check_size<std::vector<int> >::value == true) {
std::cout << "Vector has a size field!" << std::endl;
}
if (check_size<bar>::value != true) {
std::cout << "Bar doesn't have a size field!" << std::endl;
}
}
При создании экземпляра класса check_size<std::vector<int> создается
первый вариант функции check_size, поскольку можно без проблем создать
экземпляр структуры wrap.
При создании экземпляра класса check_size<bar> создается второй вариант
функции check_size, поскольку создание экземпляра структуры wrap
приводит к ошибке.
Замечание Я не совсем понял, почему нельзя первый вариант функции check_size
написать так:
template<typename T>
true_type check(T*, size_t (T::*)() const = &T::size) { }
При создании экземпляра класса check_size<bar> компилятор говорит, что
у класса bar нет метода size. В этом случае SWIFAE не работает?
Списки типов
На шаблонах можно реализовать списки в функциональном стиле.