القوالب المتغيرة
جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 67 من 104.
ماذا لو احتجت إلى function تقبل أي عدد من arguments من أي أنواع؟ تتطلب القوالب التقليدية تحديد عدد parameter للنوع الذي تحتاج إليه بالضبط. تحل القوالب متغيرة المعاملات هذه المشكلة من خلال السماح للقوالب بقبول عدد اعتباطي من معاملات القالب.
يستخدم بناء الجملة ... (نقاط الحذف) لإنشاء حزمة parameter:
template <typename... Args>
void printAll(Args... args) {
// Args هي حزمة معاملات قالب
// args هي حزمة معاملات دالة
}لمعالجة arguments، تستخدم عادةً الاستدعاء الذاتي مع base case. يتوسع parameter pack بمقدار argument واحد في كل مرة حتى لا يتبقى أي منها:
// الحالة الأساسية: لا توجد وسائط متبقية
void print() {
std::cout << std::endl;
}
// الحالة التكرارية: معالجة الوسيط الأول، والتكرار مع الباقي
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
std::cout << first << " ";
print(rest...); // توسيع الوسائط المتبقية
}
print(1, 3.14, "hello", 'x'); // المخرجات: 1 3.14 hello xيمكنك أيضًا استخدام تعبيرات الطي (C++17) لإجراء عمليات أبسط دون استخدام الاستدعاء الذاتي الصريح:
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // تعبير الطي: يضيف جميع الوسائط
}
std::cout << sum(1, 2, 3, 4) << std::endl; // المخرجات: 10تُشغّل القوالب متغيرة المعاملات العديد من ميزات المكتبة القياسية مثل std::make_unique وstd::tuple وstd::function. وهي تتيح دوال آمنة من حيث النوع تعمل مع أي مجموعة من أنواع المعاملات وأعدادها.
التحدي
سهللنبنِ نظام تسجيل مرنًا يستخدم القوالب المتغيرة للتعامل مع الرسائل التي تحتوي على أي عدد من الوسائط. ستنشئ أدوات مساعدة يمكنها ربط القيم، وعدّ الوسائط، وطباعة مخرجات منسّقة: وكل ذلك باستخدام حزم المعاملات.
ستنظّم التعليمات البرمجية عبر ملفين:
Variadic.h: عرّف دوال القوالب المتغيرة هنا.أنشئ دالة
printAllتطبع جميع وسائطها مفصولة بمسافات، متبوعة بسطر جديد. استخدم النهج التكراري مع حالة أساسية تطبع سطرًا جديدًا فقط، وحالة تكرارية تطبع الوسيطة الأولى، ثم مسافة، وبعد ذلك تستدعي نفسها مع الوسائط المتبقية.أنشئ دالة
countArgsتُرجع عدد الوسائط المُمرَّرة إليها. استخدمsizeof...للحصول على حجم حزمة المعاملات.أنشئ دالة
sumتجمع جميع الوسائط الرقمية معًا وتُرجع النتيجة. استخدم تعبير طيّ مع العامل+.أنشئ دالة
productتضرب جميع الوسائط الرقمية معًا وتُرجع النتيجة. استخدم تعبير طيّ مع العامل*.main.cpp: اقرأ أربع مدخلات، كل منها في سطر منفصل:- عدد صحيح
- عدد عشري مزدوج الدقة
- سلسلة نصية
- حرف
وضّح استخدام القوالب المتغيرة لديك من خلال:
- استدعاء
printAllباستخدام المدخلات الأربع جميعها وطباعة النتيجة - طباعة عدد الوسائط عند استدعاء
countArgsباستخدام العدد الصحيح، والعدد العشري، والحرف:Argument count: <count> - حساب مجموع العدد الصحيح، والعدد العشري، والقيمتين 10 و5، ثم طباعة:
Sum: <result> - حساب حاصل ضرب العدد الصحيح والقيمتين 2 و3، ثم طباعة:
Product: <result> - استدعاء
printAllباستخدام السلسلة النصية فقط (وسيطة واحدة) - استدعاء
printAllدون وسائط (ويجب أن يطبع سطرًا جديدًا فقط)
على سبيل المثال، مع المدخلات 5 و2.5 وHello وX:
5 2.5 Hello X
Argument count: 3
Sum: 22.5
Product: 30
Hello
لاحظ كيف تتعامل printAll مع أي عدد من الوسائط ذات الأنواع المختلطة، بدءًا من أربع وسائط وصولًا إلى صفر. تستخدم دالتا sum وproduct تعبيرات الطي لدمج جميع القيم بأناقة دون تكرار صريح. وتوضّح دالة countArgs لديك كيف يمنحك sizeof... حجم الحزمة في وقت الترجمة.
جرّب بنفسك
#include <iostream>
#include <string>
#include "Variadic.h"
using namespace std;
int main() {
// اقرأ المدخلات
int intVal;
double doubleVal;
string strVal;
char charVal;
cin >> intVal;
cin >> doubleVal;
cin >> strVal;
cin >> charVal;
// TODO: استدعِ printAll مع المدخلات الأربعة كلها
// TODO: Print argument count using countArgs with intVal, doubleVal, and charVal
// Format: "Argument count: <count>"
// TODO: احسب واطبع مجموع intVal و doubleVal و 10 و 5
// التنسيق: "Sum: <result>"
// TODO: احسب واطبع حاصل ضرب intVal و 2 و 3
// التنسيق: "Product: <result>"
// TODO: استدعِ printAll مع السلسلة فقط
// TODO: استدعِ printAll بدون وسائط
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةبناء وتجميع (Compilation) لغة C++ملفات الـ Header وملفات الـ Sourceالـ Namespaces والـ Scopeمقدمة في الـ OOP في C++الـ Classes مقابل الـ Objectsالمؤشر 'this'الـ Methods (الدوال الأعضاء)الـ Attributes (بيانات الأعضاء)أساسيات الـ Ctors والـ Dtorsمراجعة - آلة حاسبة بسيطة4خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticدوال الـ Getters والـ Settersدوال الأعضاء الثابتة (Const)الكلمة المفتاحية Mutableالدوال والمتغيرات الساكنة (Static)الدوال والفئات الصديقة (Friend)مراجعة - مدير الحساب البنكي7الوراثة
الوراثة الأساسيةمستويات الوصول في الوراثةترتيب استدعاء الـ Ctor والـ Dtorإعادة تعريف الدوال (Method Overriding)الدوال الافتراضية والـ VTableالوراثة المتعددةالوراثة الافتراضيةمراجعة - هيكلية الموظفين10نظرة عامة على STL
نظرة عامة وفلسفة STLحاويات STLالـ Iteratorsخوارزميات STLالـ Functors وتعبيرات Lambdaمراجعة - تكرار الكلمات13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factory و Abstract Factoryنمط Builderنمط Observerنمط Strategy2إدارة الذاكرة
ذاكرة Stack مقابل Heapالمؤشرات والمراجعالذاكرة الديناميكية (new/delete)المؤشرات الذكية في C++RAII في C++مراجعة - مدير المصفوفات الديناميكية5التغليف (Encapsulation)
محددات الوصول في C++محددات الوصول بشكل متعمقإخفاء المعلوماتStruct مقابل Classالأصناف المتداخلة والداخليةمراجعة - نظام سجلات الطلاب8تعدد الأشكال (Polymorphism)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالتحميل الزائد للدوال (Function Overloading)مراجعة الدوال الافتراضية (Virtual Functions)الدوال الافتراضية البحتة (Pure Virtual Functions)الأصناف المجردة (Abstract Classes)تصميم الواجهات (Interface) في C++التحويل الديناميكي (Dynamic Casting) و RTTIملخص - حاسبة الأشكال11مفاهيم OOP المتقدمة
التركيب مقابل الوراثةالـ Mixins عبر CRTPنمط Pimplمحو النوع (Type Erasure)أصناف Enum والأنواع القويةمعالجة الاستثناءات في OOPتسلسلات الاستثناءات المخصصة14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط قالب الطريقةنمط الحالةنمط التركيبRAII كنمط3المنشئات والموادم
المنشئ الافتراضيالمنشئ ذو المعاملاتمنشئ النسخمنشئ النقلقوائم تهيئة المنشئالمنشئات المفوضةتعمق في الموادمقاعدة الثلاثة / الخمسة / الصفرمراجعة - فئة String6تحميل العوامل (Operator Overloading)
مقدمة في تحميل العواملتحميل العوامل الحسابيةتحميل عوامل المقارنةعوامل Streamتحميل عامل التعيينتحميل العوامل [] و ()عوامل تحويل النوعمراجعة - Matrix Class9القوالب
قوالب الدوالقوالب الأصنافتخصيص القوالبالقوالب المتغيرةأساسيات SFINAE و Type Traitsمراجعة - الحاويات العامةتدرّب بنفسك: مترجم C++ عبر الإنترنت