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

Материал из SEWiki
Перейти к: навигация, поиск
м
Строка 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 не определено. Но эта ошибка не приводит к выдаче сообщения об ошибке и прекращению компиляции, поскольку есть нешаблонная функция, которую можно вызвать.

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

Списки типов