вычисление использования факториала обрабатывает метапрограммирование по шаблону

Я не понимаю, как эта часть кода (из Википедии) работает:

template 
struct Factorial 
{
    enum { value = N * Factorial::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
}
  • Что является этим странным шаблоном, который берет ?
  • Каков этот второй странный шаблон <>?
  • Что enumerations для?
  • Каково преимущество использования этого а не нормального факториального вычисления во время выполнения?
  • Как часто делают Вас, люди используют это? Я использовал C++ некоторое время теперь, но никогда не использовал это прежде. Насколько большой часть C++ была мной пропускающий?

Спасибо!

22
задан Georg Fritzsche 21 June 2010 в 18:54
поделиться

5 ответов

  • Что это за странный шаблон, который принимает ?

В C ++ аргументы шаблона могут быть любого типа (с префиксом class или typename ) или целые числа (с префиксом int или unsigned int ). Вот и мы во втором случае.

  • Что это за второй странный шаблон <> ?

шаблон <> struct Factorial <0> является полной специализацией шаблона класса Factorial, что означает, что 0 считается особым значением, которому соответствует собственная версия Factorial.

  • Для чего нужны перечисления?

Перечисления - это способ вычисления значений в метапрограммировании C ++

  • В чем преимущество использования этого, а не обычного вычисления факториала во время выполнения?

Причина, по которой этот код был создан в Во-первых, необходимо создать доказательство концепции, согласно которой исчисление может быть выполнено с помощью метапрограммирования. Преимущество состоит в том, что сгенерированный код чрезвычайно эффективен (вызов Factorial <4> :: value эквивалентен простому написанию «24» в вашем коде.

  • Как часто вы это используете? некоторое время использую C ++, но никогда раньше не использовал.Насколько большую часть C ++ я упустил?

Такая функциональность редко достигается с помощью этого метода, но в настоящее время метапрограммирование используется все чаще и чаще. См. Библиотека метапрограммирования Boost , чтобы получить представление о том, что можно сделать.

25
ответ дан 29 November 2019 в 04:46
поделиться

Что это за странный шаблон, который принимает ?

Объявление шаблона может быть сделано для классов / типов, значений и указателей. Обычно вы не видите эту форму, поскольку при определении шаблонов в параметрах шаблона редко используются константы.

Что это за второй странный шаблон <>?

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

То есть для:

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

Факториал <2> :: значение эквивалентно:

// generated by the compiler when Factorial<2>::value is encountered in code:
struct Factorial2 { enum { value = 2 * Factorial1::value }; };
struct Factorial1 { enum { value = 1 * Factorial0::value }; };
// not generated by compiler, as it was explicitly defined in the code you ask about:
template <> struct Factorial<0> { enum { value = 1 }; }; // defines Factorial<0>

Для чего нужны перечисления?

Они определяют перечисление со значением const во время компиляции, что позволяет вам писать клиентский код, подобный тому, что показан в вашем примере.

В чем преимущество использования этого а не обычный факториал времени выполнения расчет?

Преимущество - эффективность. Поскольку вычисление значения расширяется при компиляции, затраты времени выполнения Factorial <10> :: value такие же, как Factorial <1> :: value. В скомпилированном коде они обе являются константами. Если вам нужно было вычислить факториал в критически важном для производительности коде и вы знали во время компиляции, какое это значение, вы должны либо сделать это, либо вычислить его в автономном режиме и определить константу с предварительно вычисленным значением в вашем коде.

Как часто вы, люди, используете this?

Вы имеете в виду «this» рекурсивного вычисления константы? Если так, то не часто.

Является ли «это» определением констант в шаблонах? Очень часто :)

Является ли "это" детализацией шаблона для определенного типа? Очень-очень-очень часто. Я понял, что std :: vector имеет полностью отдельную реализацию в некоторых реализациях STL ( std :: vector с 8 элементами не требует более 1 байта для хранения значений).

Я уже давно использую C ++, но никогда раньше этим не пользовался. Насколько большой часть C ++ я упустил?

Не обязательно большая часть. Если вы используете boost, вероятно, что используемый вами код реализован с использованием таких вещей.

6
ответ дан 29 November 2019 в 04:46
поделиться

В чем преимущество использования этого вычисления по сравнению с обычным вычислением факториала во время выполнения?

Это быстрее - теоретически компилятор расширит набор шаблонов во время компиляции, так что Factorial :: value - это просто единственная константа. В некоторых исчезающе малом количестве случаев это может иметь большое значение.

Обратной стороной является то, что он должен вычисляться во время компиляции, поэтому, если ваш n определяется во время выполнения, вы не можете использовать этот метод.

Как часто вы этим пользуетесь? Я уже давно использую C ++, но никогда раньше этим не пользовался. Насколько большую часть C ++ я упускал?

Такой случай, совсем не часто. Метапрограммирование шаблонов потенциально очень мощно, но количество случаев, когда этот пример полезен и достаточно важен для производительности, очень мало.

Но это полезный метод в C ++, поскольку он позволяет языку выполнять множество мощных трюков, которые в противном случае были бы недоступны. Рискну предположить, что гораздо более распространено использование метапрограммирования шаблонов, которое кто-то сделал для вас, например. Boost использует его довольно активно и в результате может делать очень умные вещи. Это мощно, но может также запутать код, если не сделано хорошо.

3
ответ дан 29 November 2019 в 04:46
поделиться

Это рекурсия, выполняемая компилятором, где

template <>
struct Factorial<0>
{
}

- точка выхода из рекурсии.

Сначала разрешается Факториал <4> . Для значения 4 не существует специализации Factorial , поэтому используется первое определение Factorial <> . Затем его значение вычисляется с помощью Factorial <3> :: value , где повторяется предыдущий шаг.

Это будет продолжаться до тех пор, пока N == 1, затем для N-1 вступает в действие специализация Factorial <0> , где значение устанавливается равным 1. На этом рекурсия останавливается, и компилятор может пойти вверх и вычислить оставшиеся значения Factorial .

2
ответ дан 29 November 2019 в 04:46
поделиться

Я нашел этот ответ Йоханнесом Шаубом на другой вопрос очень полезным.

[ Я копирую и вставляю ответ здесь, предполагая, что Йоханнес не против. Я удалю пасту, если она ему не понравится ]


Да, это не типовой параметр. У вас может быть несколько видов параметров шаблона

  • Параметры типа.
    • Типы
    • Шаблоны (только классы, без функций)
  • Параметры, не относящиеся к типу
    • Указатели
    • Ссылки
    • Выражения интегральных констант

То, что у вас есть, относится к последнему виду. Это константа времени компиляции (так называемое постоянное выражение) и имеет целочисленный или перечисляемый тип. После поиска в стандарте мне пришлось переместить шаблоны классов в раздел типов, хотя шаблоны не являются типами. Но, тем не менее, для описания этих типов они называются параметрами типа. У вас могут быть указатели (а также указатели членов) и ссылки на объекты / функции, которые имеют внешнюю связь (те, которые могут быть связаны из других объектных файлов и чей адрес уникален во всей программе). Примеры:

Параметр типа шаблона:

template<typename T>
struct Container {
    T t;
};

// pass type "long" as argument.
Container<long> test;

Целочисленный параметр шаблона:

template<unsigned int S>
struct Vector {
    unsigned char bytes[S];
};

// pass 3 as argument.
Vector<3> test;

Параметр указателя шаблона (передача указателя на функцию)

template<void (*F)()>
struct FunctionWrapper {
    static void call_it() { F(); }
};

// pass address of function do_it as argument.
void do_it() { }
FunctionWrapper<&do_it> test;

Параметр ссылки на шаблон (передача целого числа)

template<int &A>
struct SillyExample {
    static void do_it() { A = 10; }
};

// pass flag as argument
int flag;
SillyExample<flag> test;

Параметр шаблона шаблона.

template<template<typename T> class AllocatePolicy>
struct Pool {
    void allocate(size_t n) {
        int *p = AllocatePolicy<int>::allocate(n);
    }
};

// pass the template "allocator" as argument. 
template<typename T>
struct allocator { static T * allocate(size_t n) { return 0; } };
Pool<allocator> test;

Шаблон без каких-либо параметров невозможен.Но возможен шаблон без явных аргументов - у него есть аргументы по умолчанию:

template<unsigned int SIZE = 3>
struct Vector {
    unsigned char buffer[SIZE];
};

Vector<> test;

Синтаксически template <> зарезервирован для обозначения явной специализации шаблона вместо шаблона без параметров:

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};

4
ответ дан 29 November 2019 в 04:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: