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

تخصيص القوالب

جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 66 من 104.

أحياناً لا يعمل القالب العام (generic template) بشكل جيد مع كل الأنواع. على سبيل المثال، مقارنة سلاسل النصوص بنمط C (C-style strings) باستخدام > تقارن عناوين المؤشرات، وليس النص الفعلي. يتيح لك تخصيص القالب (Template specialization) توفير تنفيذ مخصص لأنواع معينة مع الاحتفاظ بالإصدار العام لكل شيء آخر.

يحل التخصيص الكامل محل القالب بالكامل لنوع واحد محدد. تقوم بالتصريح عنه باستخدام قائمة معلمات قالب فارغة وتحدد النوع المحدد:

// القالب الأساسي
template <typename T>
class Printer {
public:
    void print(T value) {
        std::cout << value << std::endl;
    }
};

// تخصيص كامل لـ const char*
template <>
class Printer<const char*> {
public:
    void print(const char* value) {
        std::cout << "String: " << value << std::endl;
    }
};

Printer<int> intPrinter;
intPrinter.print(42);           // المخرجات: 42

Printer<const char*> strPrinter;
strPrinter.print("hello");      // المخرجات: String: hello

يمكن أيضًا تخصيص قوالب الدوال:

template <typename T>
bool isEqual(T a, T b) {
    return a == b;
}

template <>
bool isEqual<const char*>(const char* a, const char* b) {
    return std::strcmp(a, b) == 0;
}

isEqual(5, 5);              // يستخدم الإصدار العام
isEqual("hi", "hi");        // يستخدم الإصدار المخصص

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

challenge icon

التحدي

سهل

لنقم ببناء نظام منسق (formatter) مدرك للأنواع يوضح كيف يتيح لك تخصيص القوالب (template specialization) تخصيص السلوك لأنواع معينة مع الحفاظ على سلوك عام احتياطي لكل شيء آخر.

ستقوم بإنشاء ملفين لتنظيم كود القوالب الخاص بك:

  • Formatter.h: قم بتعريف قالب فئة (class template) يسمى Formatter يقوم بتنسيق القيم للعرض. يجب أن يعمل القالب العام الخاص بك مع أي نوع، ولكنك ستنشئ أيضاً إصدارات متخصصة للأنواع التي تحتاج إلى معالجة مخصصة.

    يجب أن يحتوي قالب Formatter الأساسي على:

    • دالة format() تأخذ قيمة من النوع T وتطبع: Value: <value>

    قم بإنشاء تخصيص كامل (full specialization) للنوع bool يطبع نصاً قابلاً للقراءة بدلاً من 1 أو 0:

    • يجب أن تطبع دالة format(): Boolean: true أو Boolean: false

    قم بإنشاء تخصيص كامل للنوع const char* يضيف علامات اقتباس حول النصوص:

    • يجب أن تطبع دالة format(): String: "<value>"

    قم أيضاً بإنشاء قالب دالة (function template) يسمى formatPair يأخذ قيمتين من نفس النوع ويطبعهما معاً كـ: Pair: [<first>, <second>]

    قم بإنشاء تخصيص لـ formatPair للنوع const char* يغلف كل نص بعلامات اقتباس: Pair: ["<first>", "<second>"]

  • main.cpp: اقرأ أربعة مدخلات (كل منها في سطر منفصل):
    1. عدد صحيح (integer)
    2. عدد عشري (double)
    3. قيمة منطقية كنص (true أو false)
    4. قيمة نصية (string)

    قم باستعراض المنسقات الخاصة بك عن طريق إنشاء كائنات Formatter مناسبة واستدعاء format() لكل نوع:

    1. قم بتنسيق العدد الصحيح باستخدام Formatter<int>
    2. قم بتنسيق العدد العشري باستخدام Formatter<double>
    3. قم بتنسيق القيمة المنطقية باستخدام Formatter<bool> (قم بتحويل النص المدخل إلى bool أولاً)
    4. قم بتنسيق النص باستخدام Formatter<const char*>

    ثم استعرض تخصيص قالب الدالة:

    1. استدعِ formatPair مع عددين صحيحين: 10 و 20
    2. استدعِ formatPair مع نصين من نوع C-strings: "hello" و "world"

على سبيل المثال، مع المدخلات 42، 3.14، true، و Hello:

Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]

لاحظ كيف يتعامل القالب العام مع الأعداد الصحيحة والعشرية بشكل متطابق، بينما توفر الإصدارات المتخصصة لـ bool و const char* تنسيقاً مخصصاً. يقوم المترجم (compiler) تلقائياً باختيار المطابقة الأكثر تحديداً لكل نوع تستخدمه.

ورقة مرجعية

يسمح لك تخصيص القوالب (Template specialization) بتوفير تطبيقات مخصصة لأنواع معينة مع الاحتفاظ بنسخة عامة لجميع الأنواع الأخرى.

صيغة التخصيص الكامل (Full Specialization Syntax)

قم بالتصريح عن التخصيص باستخدام قائمة معلمات قالب فارغة template <> وحدد النوع الملموس:

// Primary template
template <typename T>
class Printer {
public:
    void print(T value) {
        std::cout << value << std::endl;
    }
};

// Full specialization for const char*
template <>
class Printer<const char*> {
public:
    void print(const char* value) {
        std::cout << "String: " << value << std::endl;
    }
};

تخصيص قوالب الدوال (Function Template Specialization)

يمكن أيضاً تخصيص قوالب الدوال:

template <typename T>
bool isEqual(T a, T b) {
    return a == b;
}

template <>
bool isEqual<const char*>(const char* a, const char* b) {
    return std::strcmp(a, b) == 0;
}

اختيار المترجم (Compiler Selection)

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

جرّب بنفسك

#include <iostream>
#include <string>
#include "Formatter.h"

using namespace std;

int main() {
    // قراءة المدخلات
    int intVal;
    double doubleVal;
    string boolStr;
    string strVal;
    
    cin >> intVal;
    cin >> doubleVal;
    cin >> boolStr;
    cin >> strVal;
    
    // تحويل string إلى bool
    bool boolVal = (boolStr == "true");
    
    // TODO: قم بإنشاء Formatter<int> وتنسيق العدد الصحيح
    
    // TODO: قم بإنشاء Formatter<double> وتنسيق العدد العشري
    
    // TODO: قم بإنشاء Formatter<bool> وتنسيق القيمة المنطقية
    
    // TODO: قم بإنشاء Formatter<const char*> وتنسيق السلسلة النصية
    // تلميح: استخدم strVal.c_str() للحصول على const char*
    
    // TODO: استدعِ formatPair مع عددين صحيحين: 10 و 20
    
    // TODO: استدعِ formatPair مع سلسلتين نصيتين من نوع C-strings: "hello" و "world"
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming