Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

القوالب المتغيرة

جزء من قسم البرمجة كائنية التوجه في رحلة 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. وهي تتيح دوال آمنة من حيث النوع تعمل مع أي مجموعة من أنواع المعاملات وأعدادها.

challenge icon

التحدي

سهل

لنبنِ نظام تسجيل مرنًا يستخدم القوالب المتغيرة للتعامل مع الرسائل التي تحتوي على أي عدد من الوسائط. ستنشئ أدوات مساعدة يمكنها ربط القيم، وعدّ الوسائط، وطباعة مخرجات منسّقة: وكل ذلك باستخدام حزم المعاملات.

ستنظّم التعليمات البرمجية عبر ملفين:

  • Variadic.h: عرّف دوال القوالب المتغيرة هنا.

    أنشئ دالة printAll تطبع جميع وسائطها مفصولة بمسافات، متبوعة بسطر جديد. استخدم النهج التكراري مع حالة أساسية تطبع سطرًا جديدًا فقط، وحالة تكرارية تطبع الوسيطة الأولى، ثم مسافة، وبعد ذلك تستدعي نفسها مع الوسائط المتبقية.

    أنشئ دالة countArgs تُرجع عدد الوسائط المُمرَّرة إليها. استخدم sizeof... للحصول على حجم حزمة المعاملات.

    أنشئ دالة sum تجمع جميع الوسائط الرقمية معًا وتُرجع النتيجة. استخدم تعبير طيّ مع العامل +.

    أنشئ دالة product تضرب جميع الوسائط الرقمية معًا وتُرجع النتيجة. استخدم تعبير طيّ مع العامل *.

  • main.cpp: اقرأ أربع مدخلات، كل منها في سطر منفصل:
    1. عدد صحيح
    2. عدد عشري مزدوج الدقة
    3. سلسلة نصية
    4. حرف

    وضّح استخدام القوالب المتغيرة لديك من خلال:

    1. استدعاء printAll باستخدام المدخلات الأربع جميعها وطباعة النتيجة
    2. طباعة عدد الوسائط عند استدعاء countArgs باستخدام العدد الصحيح، والعدد العشري، والحرف: Argument count: <count>
    3. حساب مجموع العدد الصحيح، والعدد العشري، والقيمتين 10 و5، ثم طباعة: Sum: <result>
    4. حساب حاصل ضرب العدد الصحيح والقيمتين 2 و3، ثم طباعة: Product: <result>
    5. استدعاء printAll باستخدام السلسلة النصية فقط (وسيطة واحدة)
    6. استدعاء 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;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C++ عبر الإنترنت