الأدوية العامة والتصنيف في C++: لماذا ليست مجرد "قوالب"؟
عندما ينتقل المطور من Python أو Java إلى C++، غالبًا ما تخيفه كلمة "قالب" (template). تتبادر إلى الذهن ارتباطات بأقواس زاوية لا نهائية، وأخطاء ترجمة غير مفهومة، وسحر std::vector. في الواقع، الأدوية العامة في C++ (أو البرمجة العامة) هي أداة قوية للغاية تتيح كتابة الكود مرة واحدة واستخدامه مع أي نوع من البيانات. ونعم، هذا ليس نفس الشيء مثل الأدوية العامة في Java أو C#.
في هذه المقالة، سنشرح كيف يعمل التصنيف في C++ من خلال القوالب، وكيف تختلف قوالب الفئات عن قوالب الوظائف، ولماذا تمت إضافة المفاهيم في C++20. سيكون هناك العديد من الأمثلة - من البسيطة إلى تلك التي ستكون مفيدة حقًا في الإنتاج. هيا بنا!
ما هي الأدوية العامة (القوالب) في C++؟
ببساطة: القوالب في C++ هي طريقة لكتابة كود يعمل مع أي نوع من البيانات دون فقدان الأداء. على عكس التصنيف الديناميكي في Python، حيث يتم التحقق من النوع في وقت التشغيل، يقوم C++ بإنشاء نسخة منفصلة من الوظيفة أو الفئة لكل نوع مستخدم في مرحلة الترجمة. وهذا ما يسمى التصنيف الثابت مع التوسيط.
من الناحية النحوية، يتم تعريف القالب باستخدام الكلمة المفتاحية template والأقواس الزاوية مع المعاملات:
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}هنا typename T هو معامل القالب. يمكنك استخدام class بدلاً من typename - لا فرق. عندما تكتب max(3, 7)، يقوم المترجم تلقائيًا باستبدال int مكان T وينشئ الوظيفة int max(int, int). إذا كتبت max(3.14, 2.71) - ستحصل على نسخة لـ double. كل شيء بسيط وفعال.
قوالب الوظائف: الخطوة الأولى نحو الأدوية العامة
لنبدأ بشيء صغير - قوالب الوظائف في C++. هذه طريقة مثالية لتجنب تكرار الكود عندما يكون المنطق واحدًا ولكن الأنواع مختلفة.
مثال: وظيفة تطبع عنصرًا في وحدة التحكم (مفيدة لتصحيح الأخطاء):
#include <iostream>#include <string>
template <typename T>void print(const T& value) { std::cout << value << std::endl;}
int main() { print(42); // int print(3.1415); // double print("Hello"); // const char* print(std::string("Generics!")); // std::string return 0;}لاحظ: نستخدم const T& - هذا هو التصنيف في C++ مع الإرسال بمرجع ثابت لتجنب نسخ الكائنات الكبيرة. بالنسبة للأنواع المضمنة (int, double) هذا ليس ضروريًا، لكنها عادة جيدة.
ماذا لو كنت بحاجة إلى تمرير وسيطين من أنواع مختلفة؟ بسهولة:
template <typename T1, typename T2>auto add(T1 a, T2 b) -> decltype(a + b) { return a + b;}
// في C++14 وما بعده يمكن ببساطة:template <typename T1, typename T2>auto add(T1 a, T2 b) { return a + b;}هنا auto يستنتج نوع القيمة المرجعة تلقائيًا. هذا هو الأدوية العامة في العمل - كود واحد، عدد لا نهائي من تركيبات الأنواع.
قوالب الفئات: بناء هياكل عالمية
الآن ننتقل إلى قوالب الفئات في C++. هذه هي أساس المكتبة القياسية (STL): std::vector<T>, std::map<Key, Value>, std::optional<T> - كلها مطبقة من خلال القوالب.
لنكتب مثالًا بسيطًا لكنه دال - فئة قالبية "زوج" (مشابهة لـ std::pair):
template <typename First, typename Second>class Pair {private: First first_; Second second_;public: