الأنواع العامة (Generics) في C++: لماذا تحتاجها وكيف تستخدمها
مرحباً يا صديقي! إذا كنت قد بدأت للتو في تعلم C++ وقد صادفت مفهوم الأنواع العامة (Generics) أو القوالب (templates)، فهذه المقالة لك. يخاف العديد من المبتدئين من القوالب، معتبرين أنها شيء معقد وسحري. في الحقيقة، الأمر أبسط بكثير. دعنا نفهم ما هي، ولماذا نحتاجها، وكيف نستخدمها عملياً.
ما هي الأنواع العامة (القوالب) في C++؟
تخيل أنك بحاجة لكتابة دالة تجمع رقمين. تكتب دالة واحدة من أجل int، وأخرى من أجل double، وثالثة من أجل float... ستوافقني الرأي، هذا متعب وينتهك مبدأ DRY (لا تكرر نفسك).
هنا تأتي القوالب (templates) للإنقاذ. هي آلية تسمح بكتابة كود عام يعمل مع أي نوع بيانات. في C++، هذا هو ما يُسمى في لغات أخرى بـ Generics (مثل Java أو C#).
القالب ليس دالة أو كلاساً محدداً، بل هو "مخطط" أو "وصفة". عندما يرى المترجم أنك تستخدم قالباً مع نوع int، يقوم بإنشاء نسخة من الكود من أجل int. إذا استخدمته مع double، يقوم المترجم بإنشاء نسخة من أجل double. وهذا يُسمى إنشاء القالب (instantiation).
لماذا نحتاج القوالب؟ ثلاثة أسباب رئيسية
1. إعادة استخدام الكود
بدلاً من كتابة 10 دوال لأنواع مختلفة من الأرقام، تكتب دالة واحدة شاملة. هذا يوفر الوقت، ويقلل من عدد الأخطاء، ويجعل الكود أكثر قابلية للقراءة.
2. أمان الأنواع
على عكس الماكرو (#define) أو استخدام void*، يتم فحص القوالب بواسطة المترجم. إذا حاولت تمرير نوع غير مناسب إلى القالب، سيعطي المترجم خطأ في مرحلة البناء، وليس أثناء تنفيذ البرنامج.
3. الأداء
القوالب لا تضيف أي تكاليف إضافية أثناء التنفيذ. يقوم المترجم بإنشاء كود محسّن لكل نوع محدد، وهذا الكود يعمل بنفس سرعة ما لو كتبته يدوياً.
كيف تستخدم القوالب: بناء الجملة والأمثلة
دعنا ننظر إلى نوعين رئيسيين من القوالب: قوالب الدوال وقوالب الكلاسات.
قوالب الدوال
أبسط مثال هو دالة تجد القيمة القصوى من قيمتين.
#include <iostream>#include <string>
// نعلن عن قالب دالةtemplate <typename T>T max_value(T a, T b) { return (a > b) ? a : b;}
int main() { // يعمل مع int std::cout << max_value(10, 20) << std::endl; // الإخراج: 20 // يعمل مع double std::cout << max_value(3.14, 2.71) << std::endl; // الإخراج: 3.14 // يعمل مع السلاسل النصية std::cout << max_value(std::string("apple"), std::string("banana")) << std::endl; // الإخراج: banana return 0;}الكلمة المفتاحية template تخبر المترجم: "انتبه، ما يلي هو قالب". <typename T> هي قائمة معاملات القالب. T هو اسم نوع مؤقت سيتم استبداله بالنوع الحقيقي عند استدعاء الدالة. بدلاً من typename يمكنك استخدام class — لا فرق.
مهم: المترجم يخمن بنفسه النوع الذي تقصده من خلال الوسائط المُمررة. وهذا يُسمى استنتاج وسائط القالب. ولكن يمكنك تحديد النوع بشكل صريح:
std::cout << max_value<double>(10, 20.5) << std::endl; // نحدد النوع double بشكل صريحقوالب الكلاسات
قوالب الكلاسات تسمح بإنشاء حاويات بيانات عامة. المثال الكلاسيكي هو نسخة مبسطة من std::pair.
#include <iostream>#include <string>
// قالب