Подтвердить что ты не робот

Причина использования параметра типа non-type вместо обычного параметра?

В С++ вы можете создавать шаблоны с использованием параметра шаблона, отличного от типа:

template< int I >
void add( int& value )
{
  value += I;
}

int main( int argc, char** argv )
{
  int i = 10;
  add< 5 >( i );
  std::cout << i << std::endl;
}

Что печатает "15" в cout. Для чего это нужно? Есть ли какая-либо причина использования параметра шаблона непигового типа вместо более обычного, например:

void add( int& value, int amount )
{
  value += amount;
}

Извините, если это уже было задано (я посмотрел, но ничего не нашел).

4b9b3361

Ответ 1

Существует множество приложений для аргументов шаблона, отличных от типа; вот несколько:

Вы можете использовать аргументы non-type для реализации генерических типов, представляющих массивы или матрицы фиксированного размера. Например, вы можете параметризовать тип Matrix по его размерам, чтобы вы могли сделать Matrix<4, 3> или Matrix<2, 2>. Если вы затем правильно определите перегруженные операторы для этих типов, вы можете предотвратить случайные ошибки при добавлении или умножении матриц неправильных размеров и может выполнять функции, которые явно сообщают ожидаемые размеры матриц, которые они принимают. Это предотвращает появление огромного класса ошибок времени выполнения, обнаруживая нарушения во время компиляции.

Вы можете использовать аргументы non-type для реализации оценки функции компиляции с помощью метапрограммирования шаблонов. Например, здесь простой шаблон, который вычисляет факториал во время компиляции:

template <unsigned n> struct Factorial {
    enum { 
       result = n * Factorial<n - 1>::result
    };
};
template <> struct Factorial<0> {
    enum {
       result = 1
    };
};

Это позволяет вам писать код типа Factorial<10>::result, чтобы получить во время компиляции значение 10!. Это может предотвратить выполнение дополнительного кода во время выполнения.

Кроме того, вы можете использовать аргументы non-type для реализации анализа параметров времени компиляции, который позволяет вам определять типы для килограммов, метров, секунд и т.д. что компилятор может гарантировать, что вы случайно не используете килограммы, где вы имели в виду счетчики и т.д.

Надеюсь, это поможет!

Ответ 2

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

Но как насчет этого?

template <std::size_t N>
std::array<int, N> get_array() { ... }

std::array должен знать свой размер во время компиляции (поскольку он выделен в стеке).

Вы не можете сделать что-то вроде этого:

std::array<int>(5);

Ответ 3

В этом конкретном случае на самом деле нет никакого преимущества. Но используя такие параметры шаблона, вы можете сделать много чего, чего не могли бы сделать иначе, например эффективно связать переменные с функциями (например, boost::bind), указать размер массива времени компиляции в функции или классе (std::array является готовым примером этого) и т.д.

Например, с помощью этой функции вы пишете функцию типа

template<typename T>
void apply(T f) {
    f(somenum);
}

Затем вы можете передать apply функцию:

apply(&add<23>);

Это очень простой пример, но он демонстрирует принцип. Более продвинутые приложения включают применение функций к каждому значению в коллекции, вычисление таких вещей, как факториал функции во время компиляции, и многое другое.

Вы не могли бы сделать это иначе.

Ответ 4

Хорошо, это типичный выбор между полиморфизмом времени компиляции и полиморфизмом во время выполнения.

Из формулировки вашего вопроса видно, что вы не видите ничего необычного в "обычных" параметрах шаблона, хотя воспринимаете непиковые параметры как нечто странное и/или избыточное. На самом деле та же проблема может быть применена к параметрам типа шаблона (так называемые "обычные" параметры). Идентичные функции часто могут быть реализованы либо через полиморфные классы с виртуальными функциями (полиморфизм во время выполнения), либо через параметры типа шаблона (полиморфизм времени компиляции). Можно также спросить, почему нам нужны параметры типа шаблона, поскольку практически все может быть реализовано с использованием полиморфных классов.

В случае нетиповых параметров вы можете иметь что-то подобное в этот день

template <int N> void foo(char (&array)[N]) {
  ...
}

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

Ответ 5

Есть много причин, например, делать метапрограммирование шаблонов (проверьте Boost.MPL). Но нет необходимости заходить так далеко, С++ 11 std::tuple имеет аксессор std::get<i>, который нужно индексировать во время компиляции, так как результат зависит от индекса.

Ответ 6

Наиболее частое использование параметра значения, о котором я могу думать, это std::get<N>, который извлекает N-й элемент из std::tuple<Args...>. Второе наиболее частое использование было бы std::integral_constant и его основных производных std::true_type и std::false_type, которые являются повсеместными в любых классах признаков. На самом деле, черты типа полностью переполнены параметрами шаблона значений. В частности, существуют методы SFINAE, которые используют шаблон подписи <typename T, T> для проверки существования члена класса.