Я не понимаю, как эта часть кода (из Википедии) работает:
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
}
?<>
?enum
erations для?Спасибо!
- Что это за странный шаблон, который принимает
?
В C ++ аргументы шаблона могут быть любого типа (с префиксом class
или typename
) или целые числа (с префиксом int
или unsigned int
). Вот и мы во втором случае.
- Что это за второй странный
шаблон <>
?
шаблон <> struct Factorial <0>
является полной специализацией шаблона класса Factorial, что означает, что 0
считается особым значением, которому соответствует собственная версия Factorial.
- Для чего нужны перечисления?
Перечисления - это способ вычисления значений в метапрограммировании C ++
- В чем преимущество использования этого, а не обычного вычисления факториала во время выполнения?
Причина, по которой этот код был создан в Во-первых, необходимо создать доказательство концепции, согласно которой исчисление может быть выполнено с помощью метапрограммирования. Преимущество состоит в том, что сгенерированный код чрезвычайно эффективен (вызов Factorial <4> :: value
эквивалентен простому написанию «24» в вашем коде.
- Как часто вы это используете? некоторое время использую C ++, но никогда раньше не использовал.Насколько большую часть C ++ я упустил?
Такая функциональность редко достигается с помощью этого метода, но в настоящее время метапрограммирование используется все чаще и чаще. См. Библиотека метапрограммирования Boost , чтобы получить представление о том, что можно сделать.
Что это за странный шаблон, который принимает
?
Объявление шаблона может быть сделано для классов / типов, значений и указателей. Обычно вы не видите эту форму, поскольку при определении шаблонов в параметрах шаблона редко используются константы.
Что это за второй странный шаблон <>?
Лучший способ думать о шаблоне - это делать что-то похожее на то, что делает компилятор: рассматривать шаблон как класс, в котором вы заменяете параметр шаблона фактическим значение этого типа.
То есть для:
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, вероятно, что используемый вами код реализован с использованием таких вещей.
В чем преимущество использования этого вычисления по сравнению с обычным вычислением факториала во время выполнения?
Это быстрее - теоретически компилятор расширит набор шаблонов во время компиляции, так что Factorial
- это просто единственная константа. В некоторых исчезающе малом количестве случаев это может иметь большое значение.
Обратной стороной является то, что он должен вычисляться во время компиляции, поэтому, если ваш n
определяется во время выполнения, вы не можете использовать этот метод.
Как часто вы этим пользуетесь? Я уже давно использую C ++, но никогда раньше этим не пользовался. Насколько большую часть C ++ я упускал?
Такой случай, совсем не часто. Метапрограммирование шаблонов потенциально очень мощно, но количество случаев, когда этот пример полезен и достаточно важен для производительности, очень мало.
Но это полезный метод в C ++, поскольку он позволяет языку выполнять множество мощных трюков, которые в противном случае были бы недоступны. Рискну предположить, что гораздо более распространено использование метапрограммирования шаблонов, которое кто-то сделал для вас, например. Boost использует его довольно активно и в результате может делать очень умные вещи. Это мощно, но может также запутать код, если не сделано хорошо.
Это рекурсия, выполняемая компилятором, где
template <>
struct Factorial<0>
{
}
- точка выхода из рекурсии.
Сначала разрешается Факториал <4>
. Для значения 4 не существует специализации Factorial
, поэтому используется первое определение Factorial <>
.
Затем его значение вычисляется с помощью Factorial <3> :: value
, где повторяется предыдущий шаг.
Это будет продолжаться до тех пор, пока N == 1, затем для N-1 вступает в действие специализация Factorial <0>
, где значение устанавливается равным 1. На этом рекурсия останавливается, и компилятор может пойти вверх и вычислить оставшиеся значения Factorial
.
Я нашел этот ответ Йоханнесом Шаубом на другой вопрос очень полезным.
[ Я копирую и вставляю ответ здесь, предполагая, что Йоханнес не против. Я удалю пасту, если она ему не понравится ]
Да, это не типовой параметр. У вас может быть несколько видов параметров шаблона
То, что у вас есть, относится к последнему виду. Это константа времени компиляции (так называемое постоянное выражение) и имеет целочисленный или перечисляемый тип. После поиска в стандарте мне пришлось переместить шаблоны классов в раздел типов, хотя шаблоны не являются типами. Но, тем не менее, для описания этих типов они называются параметрами типа. У вас могут быть указатели (а также указатели членов) и ссылки на объекты / функции, которые имеют внешнюю связь (те, которые могут быть связаны из других объектных файлов и чей адрес уникален во всей программе). Примеры:
Параметр типа шаблона:
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
};