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

Материал из SEWiki
Перейти к: навигация, поиск
Строка 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 (SWINAE) ==
+
== 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 не работает?

Списки типов

На шаблонах можно реализовать списки в функциональном стиле.