c++ - وظيفة قالب مضروب دون التخصص قالب
templates recursion (2)
المشكلة هنا هي أن عبارة if هي عبارة عن بناء لوقت التشغيل. عندما يكون لديك
int f() {
if (N == 1) return 1; // we exit the recursion at 1 instead of 0
return N*f<N-1>();
}
يتم إنشاء مثيل لـ
f<N-1>
حيث يمكن استدعاؤه.
على الرغم من أن شرط if سيوقفه عن استدعاء
f<0>
، لا يزال على المحول البرمجي إنشاء مثيل له لأنه جزء من الوظيفة.
هذا يعني أنه يشبه
f<4>
، والذي يشبه
f<3>
، والذي يشبه
f<2>
، وسيستمر تشغيله إلى الأبد.
تتمثل طريقة Pre C ++ 17 لإيقاف هذا في استخدام تخصص لـ
0
الذي يكسر هذه السلسلة.
بدءًا من C ++ 17 باستخدام
constexpr if
، لم تعد هناك حاجة لذلك.
عن طريق
int f() {
if constexpr (N == 1) return 1; // we exit the recursion at 1 instead of 0
else return N*f<N-1>();
}
يضمن
return N*f<N-1>();
لن يوجد حتى في الحالة
1
، لذلك لا تستمر في النزول في فتحة أرانب مثيل.
أنا لا أفهم السلوك التالي.
التعليمات البرمجية التالية ، التي تهدف إلى حساب العامل في وقت الترجمة ، لا يتم تجميعها حتى:
#include <iostream>
using namespace std;
template<int N>
int f() {
if (N == 1) return 1; // we exit the recursion at 1 instead of 0
return N*f<N-1>();
}
int main() {
cout << f<5>() << endl;
return 0;
}
ويلقي الخطأ التالي:
...$ g++ factorial.cpp && ./a.out
factorial.cpp: In instantiation of ‘int f() [with int N = -894]’:
factorial.cpp:7:18: recursively required from ‘int f() [with int N = 4]’
factorial.cpp:7:18: required from ‘int f() [with int N = 5]’
factorial.cpp:15:16: required from here
factorial.cpp:7:18: fatal error: template instantiation depth exceeds maximum of 900 (use ‘-ftemplate-depth=’ to increase the maximum)
7 | return N*f<N-1>();
| ~~~~~~^~
compilation terminated.
بينما ، عند إضافة التخصص لـ
N == 0
(الذي لا يصل القالب أعلاه) ،
template<>
int f<0>() {
cout << "Hello, I'm the specialization.\n";
return 1;
}
يتم تجميع الشفرة وإعطاء الإخراج الصحيح لـ ، حتى إذا لم يتم استخدام التخصص:
...$ g++ factorial.cpp && ./a.out
120
المشكلة هي أن
f<N>()
دائمًا ما
ينشأ عن
f<N-1>()
ما إذا كان ذلك إذا تم أخذ الفرع أم لا.
ما لم يتم إنهاؤها بشكل صحيح ، سيؤدي ذلك إلى إنشاء تكرار غير محدود في وقت التحويل البرمجي (أي أنه سيحاول إنشاء مثيل لـ
F<0>
، ثم
f<-1>
، ثم
f<-2>
وهكذا).
من الواضح أنه يجب عليك إنهاء تلك العودية بطريقة أو بأخرى.
بصرف النظر عن حل
constexpr
والتخصص الذي اقترحه NathanOliver ، يمكنك إنهاء العودية بشكل صريح:
template <int N>
inline int f()
{
if (N <= 1)
return 1;
return N * f<(N <= 1) ? N : N - 1>();
}
ضع في اعتبارك أن هذا الحل ضعيف إلى حد ما (يجب تكرار نفس الحالة الطرفية مرتين) ، أكتب هذه الإجابة فقط لإظهار أن هناك دائمًا طرق أكثر لحل المشكلة: -)