Menu
Coddy logo textTech

תבניות ארגומנטים משתנות

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 67 מתוך 104.

מה אם תצטרכו פונקציה שמקבלת מספר כלשהו של ארגומנטים מכל סוג? תבניות מסורתיות מחייבות אתכם לציין בדיוק כמה פרמטרי טיפוס אתם צריכים. תבניות וריאדיות פותרות זאת בכך שהן מאפשרות לתבניות לקבל מספר שרירותי של ארגומנטים של תבנית.

התחביר משתמש ב־... (שלוש נקודות) כדי ליצור חבילת פרמטרים:

template <typename... Args>
void printAll(Args... args) {
    // Args הוא חבילת פרמטרים של תבנית
    // args הוא חבילת פרמטרים של פונקציה
}

כדי לעבד את הארגומנטים, בדרך כלל משתמשים ברקורסיה עם מקרה בסיס. חבילת הפרמטרים מתרחבת בארגומנט אחד בכל פעם, עד שלא נשארים ארגומנטים:

// מקרה בסיס: לא נותרו ארגומנטים
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. מספר ממשי מסוג double
    3. מחרוזת
    4. תו

    הדגימו את התבניות הווריאדיות שלכם באמצעות:

    1. קריאה ל-printAll עם כל ארבעת הקלטים והדפסת התוצאה
    2. הדפסת מספר הארגומנטים בקריאה ל-countArgs עם המספר השלם, המספר מסוג double והתו: Argument count: <count>
    3. חישוב הסכום של המספר השלם, המספר מסוג double והערכים 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: הדפס את מספר הארגומנטים באמצעות countArgs עם intVal, doubleVal ו־charVal
    // פורמט: "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++ אונליין