יום חמישי

Templates

נניח שאני רוצה לכתוב מתודה שמחברת שני מספרים. היא תראה כנראה כך:
int add(int a, int b){
return(a+b);
}
קל ופשוט? אך מה אם ארצה לחבר משתנים מסוג float? אצטרך כנראה לכתוב מתודה נוספת. דומה מאד למתודה הנ"ל, אבל עם data type שונה. זה יראה כך:


float add(float a, float b){
return(a+b);
}



ב-C++ אפשר לחסוך את הכתיבה הכפולה! במקום זה, כותבים את המתודה או ה-class עם template שיכול להתאים לכל סוגי הנתונים.
נדבר תחילה על function templates ואח"כ נעבור ל-class templates.


השימוש ב-Templates (תבניות) מובנה ב-C++ ומאפשר הגדרה של מתודות או classes שיוכלו להתאים למספר סוגים של data. למשל, אפשר לכתוב מתודה שמחברת משתנים. המתודה הזו תתאים למשתנים מכל הסוגים: int, double, float וכו. 
אז נתחיל כאמור עם function templates  ונחזור לבעיה שהצגנו קודם: איך לכתוב פונקציה שמחברת שני מספרים, ותוכל להתאים לכל סוגי הנתונים?
הנה הפתרון:

  1. template <class T>
  2. T adder(T a, T b){
  3.       T c;
  4.       c = a+b;
  5.       return c;
  6. }


הגדרנו כאן פונקציה בשם adder עם template parameter ששמו T. השם T לא מחייב. היה אפשר להחליף את T בכל סימון אחר, למשל  foofoo. 
שורה 1- ההכרזה על ה-template - חייבת להיות לפני המתודה. 
שורה 2 ואילך: מכאן משתמשים ב-template parameter כמו ב-type רגיל.

נדגים את השימוש ב-template. נניח שדרוש חיבור מסוג double:

  1. double a = 1;
  2. double b = 2;
  3. double c;
  4. c = adder<double>(a,b);


ואם רוצים לחבר int:
  1. int ia  =1;
  2. int ib = 2;
  3. int ic;
  4. ic = adder<int>(ia, ib);


שורה 4: ה-type שנשתמש בו מופיע בין הסוגריים המשולשים.
אבל אפשר לוותר גם על הסימון הזה. הקומפיילר כבר מבין מתוך הסוג של המשתנים מה ה-type. כלומר אפשר לכתוב את שורה 4 גם כך:

ic = adder(ia, ib);


אפשר לצור פונקציות עם יותר מסוג אחד של template parameter למשל:

  1. template <class T1, class T2, class T3>
  2. T3 adder(T1  a, T2  b){
  3.       T3  c;
  4.       c = a+b;
  5.       return c;
  6. }

ועכשיו נקרא לפונקציה:

int a = 1;
float b = 2;
double c;
c = adder<int, float, double>(a,b);

וגם הרישום המקוצר ללא ציון מפורש של ה-template type תקין:
c = adder(a,b);

באופן דומה אפשר להשתמש גם ב-class templates ולהקצות את סוג המשתנה של חברי ה-class בעזרת template type.

דוגמא:

  1. template <class T>
  2. class Example{
  3.       T ca;
  4.       T cb;
  5.       Example( T a, T b){
  6.       ca = a;
  7.       cb = b;
  8.      }
  9. T adder();
  10. };
את המתודה adder כבר פיתחנו קודם. הנה היא שוב, חברה ב-Example class:


  1. template <class T>
  2. T  Example<T>::adder(){
  3.       T c;
  4.       c = ca+cb;
  5.       return c;
  6. }


כעת נוכל להחליף את T בכל type שנרצה. לדוגמא, ניצור אובייקט מסוג Example ונחליף את T ב-int:

Example<int>(1 ,2);

 נציג מספר אפשרויות נוספות לשימוש ב-templates:
1. Default Types.
2. Non-Type arguments
3. Template Specialization

הסברים ודוגמאות על 3 סוגי ה-template הנ"ל:
Default Types:

דוגמא: 
  1. template <class T1, class T2 = int>
  2.  class Example{
  3.       T1  a;
  4.       T2  b;
  5.       .
  6.       .
  7. }
שורה 1: אמנם T2 מוגדר כ- type template, אך ערך ה-default שלו הוא int. 
כעת ניתן לצור אובביקט בשתי דרכים.
החלפה רגילה של ה-type template:
Example <int, int> example;
והסתמכות על ערך ה-default של T2, כך שרק T1 יחשב type taemplate:
Example<int> example;


Non Type Arguments

שימוש ב-template עם types רגילים. כאן ה-template הוא לא של type אלא של value.דוגמא:

  1. template <class T1, int Value>
  2.  class Example{
  3.       T1  a;
  4.       public:
  5.      template <class T1, int T2>
  6.      Example(){T1 = Value;}
  7. }
וכעת נדגים יצירת אובייקט מסוג Example:
Example<int, 1> example;

התוצאה: 
int a = 1;

Template Specialization

כאן מדובר בהגדרה של class שיווצר עם type מסוים.
נניח שיש לנו class עם type template. במצב זה נוכל להחליף את ה type template בכל type שנבחר. אבל נניח שאנו רוצים לתת טיפול מיוחד ל-type מסויים.
נציג כאן דוגמא שמגדירה template, אבל מגדירה טיפול מיוחד עבור type מסוג int.
הנה הדוגמא:

  1. /*
  2.  * specialization.cpp
  3.  *
  4.  *  Created on: Mar 16, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #include <iostream>
  8. using namespace std;

  9. template <class T>
  10. class Example {
  11.     T ca;
  12.     T cb;
  13.   public:
  14.     Example (T a, T b) {ca= a; cb = b;}
  15.     T cdiv (){return(ca / cb);}
  16. };

  17. template <>
  18. class Example <int> {
  19.     int ca;
  20.     int cb;
  21.   public:
  22.     Example (int a, int b) {ca = a; cb = b;}
  23.     int csum(){return (ca + cb); }
  24. };

  25. int main(){
  26.   Example<double> template_type(7, 8);
  27.   Example<int> special_type(7, 8);
  28.   cout << "\noutput of  a type template: "<< template_type.cdiv();
  29.   cout << "\noutput of  a specialization template: " << special_type.csum();
  30.   return 0;
  31. }

בצבע ירוק - class template עם שני משתנים: ca וcb. ל-class יש מתודה לחילוק שני המשתנים. כעת נוכל להחליף את T בכל type שנבחר. אבל, נניח שעבור int אנו מעוניינים להגדיר מתודת חיבור במקום חילוק. זהו ה-specialization. ראה מימוש בשורות 21-28.

ה-main מדגים יצירה של אובייקט של ה-class template  הרגיל, עם type מסוג double - שורה 31.
והוא מדגים אובייקט עם type specializtion - כשהוא משתמש ב-int - שורה 32.
שורה 33 מפעילה את המתודה cdiv, המוגדרת עבור ה-class template הרגיל.
שורה 34 מפעילה את המתודה csum המוגדרת עבור type מסוג int בלבד.

ה-constructors הכניסו את הערכים 7 ו-8 : שורות 31-32.
הנה הפלט:


output of  a type template: 0.875
output of  a specialization template: 15







2 תגובות:

  1. שאלה אליך -
    האם זה הנושא שמכליל בעצם את Inheriting Template Classes?
    או שזה נושא שעדיין לא רשום פה בבלוג?

    ושאלות אלייך -
    שרשמת"את המתודה adder כבר פיתחנו קודם. הנה היא שוב, חברה ב-Example class:"
    T Example::adder(){

    מה זה בעצם אומר שעשית את ה ADDER ככה אחרי הנקודותיים? זה בעצם כניסה לפונקציה ADDER?

    בעצם מה שעשית שם ולפני תן לי לראות אם הבנתי.

    הגדרת טמפלייט שאחת הפונקציות שלו בPUBLIC היא ADDER(). שהיא בעצם ראיתו טיפה מעל מה היא עושה.
    לאחר מכן הגדרת את סוגי המשתנים שיתאימו גם לטמפלייט בעצם בדרך TEMPLATE::ADDER)(


    חוץ מזה חייב לציין שהבלוג מצויין תודה רבה על הכל !!!

    השבמחק
  2. תודה רבה!!!
    ברור ובהיר!

    השבמחק