что такое использование шаблонных значений типа c ++? - PullRequest
2 голосов
/ 18 ноября 2010

Какая польза от нетипичных значений шаблона c ++? Что можно сделать с этим:

template <int I>
class MyClass
{
public:
    MyClass();
    .
    . //Use I;
    .
}

, что нельзя сделать с этим:

class MyClass
{
    int I;
public:
    MyClass(int i) : I(i) {}
    .
    . //Use I
    .
}

Мне кажется, что версия шаблона создает ненужные издержки компилятора, создавая два отдельных типа объектов, с отдельными копиями каждого метода класса.

Ответы [ 9 ]

8 голосов
/ 18 ноября 2010

Одно из применений заключается в том, что для отработки I можно использовать вывод аргументов шаблона, избавляя программиста от беспокойства:

template<typename T, size_t N>
T *end(T (&ra)[N]) {
    return ra + N;
}

int main() {
    std::string headings[] = {"name", "dob", "address"};
    std::ostream_iterator<std::string> output(std::cout, "\t")
    std::copy(headings, end(headings), output);
    // or
    std::vector<std::string> headingvec(headings, end(headings));

}

Не связывайтесь с sizeof каждый раз, когда вы хотите использовать массив.

Я вполне уверен, что первоначальная мотивация для этого была для шаблонов классов, таких как std::bitset, хотя, как уже упоминали другие.

6 голосов
/ 18 ноября 2010

С помощью шаблона вы можете сделать это:

template <int I>
class MyClass
{
   int array[I];
}
3 голосов
/ 18 ноября 2010

создание отдельных типов является действительно полезным идом (гугл "int to type idiom").Но помимо этого, версия шаблона позволяет компиляции знать значение числа во время компиляции, а не во время выполнения.Это означает, что существует другой набор возможных оптимизаций, которые доступны.На самом деле возможности огромны, эта функция в основном делает шаблоны с ++ самим полноценным вычислительным языком.

Для базового примера общего использования, предположим, что вы хотели сделать контейнер с фиксированным размером, известным во время компиляции.Вы не могли бы реализовать это без нетиповых параметров шаблона.Как то так:

array<int, 10> x; // create an object 10 int's big
2 голосов
/ 18 ноября 2010

С не шаблонными параметрами шаблона вы действительно можете сделать много вещей.Одним из таких примеров, обычно связанных с шаблонным метапрограммированием (см. Книгу Андрея Александреску об этом: «Современный дизайн C ++»), является шаблонный класс, который вычисляет факториал во время компиляции.Что-то вроде

template<int N>
class factorial
{
 public:
     enum { value = N * factorial<N-1>::value };
}

Тогда вы можете полностью специализировать свой факториальный класс для 0, так что он фактически где-то заканчивается:

template<>
class factorial<0>
{
 public:
     enum { value = 1 };
}

Затем вы можете использовать этот класс для вычисления некоторого факториала при компиляции-время как это:

int f4 = factorial<4>::value; // f4 will be 24 at compile time. Neat!

Это может быть не самый полезный пример, но вы получите картину.

Этот пример вы можете найти в Википедии, где вы также можете прочитать больше напредмет.

ура.

2 голосов
/ 18 ноября 2010

Разница в том, что версия шаблона расширяется во время компиляции, поэтому целое число компилируется в неявную константу; в не шаблонной версии целое число все еще существует во время выполнения. В шаблонной версии MyClass<1> и MyClass<2> - это два разных несовместимых типа, и попытка присвоить один другому вызовет ошибку компилятора.

Типичным примером этого является общий векторный класс (математический вектор, а не std::vector), где большинство методов работают одинаково, независимо от размерности вектора (добавление двухмерных векторов и четырехмерных векторов - это точно одна и та же операция ), но некоторые из них определены только для особых случаев (перекрестное произведение определено только для векторов с 3 и 7 пространствами). Если бы вы сохраняли размерность вектора в переменной-члене, вам пришлось бы выполнять проверку во время выполнения каждый раз, когда нужно выполнить операцию с возможно несовместимыми аргументами (например, добавив вектор с 2 пробелами к вектору с 4 пробелами), и вам придется обрабатывать возникающую ошибку во время выполнения.

2 голосов
/ 18 ноября 2010

Количество вещей, которые может использовать такая конструкция, бесконечно.Одним из базовых примеров будет boost :: array, который указывает, насколько большим он будет через нетипичный параметр шаблона.Было бы невозможно сделать то же самое любым другим способом (тип агрегата, содержимое в стеке).

1 голос
/ 18 ноября 2010

Одно использование - метапрограммирование .

Примером (бесстыдно украденным из википедии) является шаблон, который вычисляет мощность числа во время компиляции:

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}
0 голосов
/ 18 ноября 2010

Иллюстрация в качестве примера.

В данный момент я работаю над структурой Trie (кстати, если кто-то знает хорошую реализацию ...).

Trie в основномN-арное дерево, первый уровень которого в основном завершен, а затем становится более разреженным и более глубоким, чем глубже вы углубляетесь (это не является правильной структурой, просто в большинстве словарей короткие слова встречаются намного длиннее, чем в более длинных).

Я думал об использовании этого умения шаблонов, чтобы минимизировать количество выделенных узлов.Без шаблонов это будет происходить:

class Node3; // contains N Node2
class Node2; // contains N Node1
class Node1; // contains N Node0
class Node0; // contains N Node
class Node;  // contains N POINTERS to Node

С шаблоном, который я делаю:

template <size_t L> class Node; // contains N Node<L-1>
template <> class Node<0>;      // contains N Node
class Node;                     // contains N POINTERS to Node

И избавляет себя от скучной задачи написания макроса или копирования кода снова и снова.

0 голосов
/ 18 ноября 2010

полезно, если вы хотите определить "константы" для типов ...

например ..

template <typename _foo, int _id>
class field
{
public:

  int id() { return _id; }

};

теперь, когда вы объявляете экземпляр поля, вы можете закодировать его идентификатор ...

field<int, 10> _f1;
field<double, 11> _f2;

и т.д.. почему это полезно? рассмотреть протоколы, используемые сторонними организациями, такие как биржи (скажем, FAST / FIX и т. д.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...