Метапрограммирование на C++ — различия между версиями

Материал из SEWiki
Перейти к: навигация, поиск
(Новая страница: «TODO»)
 
Строка 1: Строка 1:
TODO
+
Мы рассмотрим несколько техник использования шаблонов 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>);

Substitution Failure Is Not An Error (SWINAE)

Проверка наличия метода у класса

Списки типов