2014-11-10 3 views
1

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

И все путаешь меня являются

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

  2. А что, если N действительно будет очень большим числом? Есть ли файл ограничение размера в исходном коде?

или что-то хорошее, чтобы знать, а не осуществлять?

#include <iostream> 
using namespace std; 

template<int N> 
class Factorial { 
public: 
    static const int value = N * Factorial<N-1>::value; 
}; 
template <> 
class Factorial<1> { 
public: 
    static const int value = 1; 
}; 

int main() { 
    Factorial<5L> f; 
    cout << "5! = " << f.value << endl; 

    } 

Выход:

5! = 120 

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

Factorial<12> f1; // works 
    Factorial<13> f2; // doesn't work 

ошибка:

undefined reference to `Factorial<13>::value' 

является его как, может идти вверх-t o 12 глубина не дальше?

+3

Как я упомянул в пункте «3» моего [здесь] (http://stackoverflow.com/a/21519186/1708801) в современном C++, многие из этих проблем метапрограмм шаблона могут быть лучше решены с использованием функций constexpr , В статье я ссылаюсь на обширное обсуждение многих компромиссов. –

+0

В этом случае 'N' не может быть большим числом, потому что вы быстро получите целочисленное переполнение. –

+0

Также обратите внимание, что большинство компиляторов имеют ограничение глубины шаблона (обычно 256). Если вы повторяете слишком много раз, вы получите ошибку времени компиляции. Есть варианты, которые могут повысить этот предел. (Конечно, здесь вы собираетесь переполнить тип 'int' задолго до достижения предела - 256! - это 507 десятичных цифр, но я просто хотел, чтобы вы знали, что существует такой предел рекурсии шаблона.) – cdhowie

ответ

5

Ответ на вопрос 1 заключается в том, что это зависит от того, что это зависит от того, как это связано: template meta programming по существу предполагает компромисс между выполнением расчета во время компиляции с выгодой, которую он не должен выполнять во время выполнения. В общем, этот метод может привести к затруднению чтения и ведения кода. Таким образом, ответ в конечном счете зависит от вашей потребности в более быстрой производительности во время работы в более медленное время компиляции и, возможно, более сложного для поддержания кода.

В статье Want speed? Use constexpr meta-programming! объясняется, как в современном C++ вы можете использовать constexpr functions много раз в качестве замены метапрограмм шаблона. Это в целом приводит к тому, что код более читабельен и, возможно, быстрее. Сравните этот шаблон мета метод программирования к constexpr например:

constexpr int factorial(int n) 
{ 
    return (n == 0 ? 1 : n*factorial(n-1)) ; 
} 

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

Стоит также отметить, что, так как результат будет быстро переполняться value, что еще одно преимущество constexpr является то, что undefined behavior is not a valid constant expression и по крайней мере текущие реализации gcc и clang повернет неопределенное поведение в constexpr в ошибку для большинство случаев.Например:

constexpr int n = factorial(13) ; 

для меня генерирует следующее сообщение об ошибке:

error: constexpr variable 'n' must be initialized by a constant expression 
constexpr int n = factorial(13) ; 
      ^ ~~~~~~~~~~~~~ 
note: value 6227020800 is outside the range of representable values of type 'int' 
return (n == 0 ? 1 : n*factorial(n-1)) ; 
        ^

Вот почему вам пример:

Factorial<13> f2; 

терпит неудачу, потому что константа требуется и gcc 4.9 дает полезная ошибка:

error: overflow in constant expression [-fpermissive] 
static const int value = N * Factorial<N-1>::value; 
       ^

, хотя более старые версии gcc дают вам менее полезную ошибку, которую вы видите.

На вопрос 2 у компиляторов есть предел рекурсии шаблона, который обычно можно настроить, но в конечном итоге у вас не будет ресурсов системы. Например, флаг для gcc будет -ftemplate-depth=n:

Set the maximum instantiation depth for template classes to n. A limit on the template instantiation depth is needed to detect endless recursions during template class instantiation. ANSI/ISO C++ conforming programs must not rely on a maximum depth greater than 17 (changed to 1024 in C++11). The default value is 900, as the compiler can run out of stack space before hitting 1024 in some situations.

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

Смежные вопросы