اخترت أن أشرح هذا التابع الموجود في مكتبة algorithm في ال STL لأهميته ولأوضح بعض الأمر المهمة في كيفية استخدامه للاستفادة التامة منه .
طبعا المكتبة algorithm تحتوي على العديد من التوابع الهامة والمفيدة والتي يجب على كل مبرمج السي++ استخدامها .
من اسم التابع السابق : هو عبارة عن حلقة من أجل كل عنصر في ال container يقوم بتنفيذ أمر معين . مثلا طباعة العناصر في ال list أو في ال vector أو تنفيذ أي شئ آخر نحدده نحن .
أولا التابع يحتوي على البارامترات التالية :
template
Fun for_each(InIt first, InIt last, Fun f);التابع قد تم تعريفه كقالب أو template وممكن أن يقبل أي نوع من أنواع البارامترات ولكن لا يذهب تفكيرنا بعيدا حتى نرى كود التابع السابق . من أسماء البارامترات ممكن أن نستنتج :
first و last عبارة عن Input Iterator أي ممكن أن تأخذ أي iterator بالإضافة إلى standard input iterators . وهي تحدد المجال الذي نريد أن ننفذ الحلقة عليه وهو دائما [first, last) أي أن last تكون بعد آخر عنصر ، وهذا هو الافتراضي دوما في ال stl .
f عبارة عن التابع الذي سوف ينفذ من أجل كل عنصر .
كود هذا التابع موجود في المكتبة algorithm وهو كالتالي :
// TEMPLATE FUNCTION for_each
template< class InIt, class Fun> inline
Fun for_each(InIt _F, InIt _L, Fun _Op)
{
for (; _F != _L; ++_F)
_Op(*_F);
return (_Op);
}من الكود السابق نلاحظ أنه يقوم بتنفيذ حلقة عادية مثل التي نكتبها عندما نريد أن نمشي في ال list أو ال vector . طبعا من الكود نجد أنه لا يمكن أن نمرر أي نوع كان في البارمترات لأن النوع InIt يجب أن يقبل المعاملين ++ و != وأيضا يجب أن يقبل المعامل * لأن طريقة الاستدعاء هي كما نلاحظ _Op(*_F); . بعد الانتهاء من الحلقة نلاحظ أن التابع for_each يرجع قيمة التابع الذي مررناه وسوف نرى استخدام لهذا لاحقا .
الآن بكل بساطة ممكن أن نرى المثال التالي :
#include
#include
#include
using namespace std;
void PrintInt(int obj)
{
cout << obj << endl;
}
void main()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for_each(v1.begin(), v1.end(), PrintInt);
}الكود السابق يقوم بطباعة كل من الأعداد 1 و 2 و 3 .. لاحظ أنه قمنا بتعريف التابع PrintInt من أجل ذلك . طبعا هذا غير عملي أبدا وإذا أردنا أن نطبع من مصفوفة char مثلا لأردنا كتابة التابع PrintChar الذي يكون تماما مثل التابع PrintInt . الحل لهذا هو استخدام ال template في تعريف ال تابع PrintInt .. ونلاحظ الكود التالي :
#include
#include
#include
using namespace std;
template
void PrintObject(T obj)
{
cout << obj << endl;
}
void main()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for_each(v1.begin(), v1.end(), PrintObject);
}الكود السابق صحيح من ناحية الترجمة والمعنى اللغوي ولكن عند الربط فإن مترجم الفجوال سي++ لم يقوم بوضع كود لـ PrintObject وذلك لاعتقاده أننا لم نستخدمها . وبالتالي أخفق الكود السابق في مرجة ال link .
ممكن أن نتحايل ونضع تعريف التابع داخل struct ونستخدم المعامل () من أجل ذلك كالتالي :
#include
#include
#include
using namespace std;
template
struct PrintObject {
void operator()(const T obj) const
{
cout << obj << endl;
}
};
void main()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for_each(v1.begin(), v1.end(), PrintObject());
}لاحظ أننا مررنا المعامل () في البارامتر الأخير في التابع for_each .
الكود السابق يعمل تماما وهكذا يمكن استخدام المعامل في الstruct PrintObject مع أي نوع بيحث يقبل معامل الاخراج إلى الشاشة .
لاحظ أننا يجب أن نحدد نوع الكائن الذي نمرره في PrintObject . طبعا بتعديل بسيط على الكود السابق ممكن أن نحسن فيه بأن نجعل المترجم يكتشف نوع الكائن بشكل آلي إذا قمنا بادخال تعريف ال template إلى داخل ال struct كالتالي :
#include
#include
#include
using namespace std;
struct PrintObject {
template
void operator()(const T obj) const
{
cout << obj << endl;
}
};
void main()
{
vector v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for_each(v1.begin(), v1.end(), PrintObject());
}وهكذا ليس من الداعي تحديد النوع في PrintObject ويمكن استخدام ال PrintObject مع for_each من أجل طباعة أي container لأي نوع في ال STL .
بما أن التابع for_each يرجع البارامتر الثالث فبالامكان أن ننفذ عنصر نحدده نحن بعد for_each كالتالي :
for_each(v1.begin(), v1.end(), PrintObject())(500);
التعديل السابق يعنني أننا سوف نطبع عناصر ال v1 كالعادة وبعدها سوف نطبع القيمة 500 ..
طبعا ممكن استخدام for_each ليس فقط مع ال iterators .. مثلا ممكن أن نستخدمه في طباعة محتويات مصفوفة عناصرها من نوع معين وليكن int . الكود التالي يوضح ذلك :
int array[3] = {1, 2, 3};
for_each(&array[0], &array[3], PrintObject());