constexpr ו־consteval
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 87 מתוך 104.
ב-C++ אפשר לבצע חישובים בזמן הקומפילציה במקום בזמן הריצה, מה שעשוי לשפר משמעותית את הביצועים. מילת המפתח constexpr (C++11) ומילת המפתח consteval (C++20) מאפשרות לך לשלוט במועד שבו ביטויים מוערכים.
ניתן לחשב פונקציית constexpr בזמן הידור אם מעבירים לה ארגומנטים קבועים, אך היא יכולה גם לפעול בזמן ריצה עם קלטים שאינם קבועים:
#include <iostream>
constexpr int square(int n) {
return n * n;
}
int main() {
constexpr int compileTime = square(5); // מחושב בזמן הקומפילציה
int x = 7;
int runtime = square(x); // מחושב בזמן הריצה
std::cout << compileTime << "\n"; // 25
std::cout << runtime << "\n"; // 49
}כשצריך להבטיח חישוב בזמן הקומפילציה, השתמשו ב-consteval. פונקציית consteval חייבת להפיק קבוע — קריאה לה עם ערכים בזמן ריצה היא שגיאת קומפילציה:
consteval int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5); // OK: 120 חושב בזמן הקומפילציה
// int x = 5;
// int bad = factorial(x); // ERROR: x אינו קבוע
}אפשר להשתמש ב-constexpr גם עם משתנים ואפילו עם בנאים של מחלקות, וכך ליצור אובייקטים שלמים בזמן הידור. הדבר שימושי במיוחד עבור טבלאות חיפוש, ערכי תצורה או כל נתון שאינו משתנה במהלך הרצת התוכנית.
אתגר
קלתארגנו את הקוד שלכם בשלושה קבצים:
MathUtils.h: הגדירו את פונקציות המתמטיקה שלכם לחישוב בזמן הידור.צרו את הפונקציות הבאות:
cube: פונקצייתconstexprשמקבלתintומחזירה את החזקה השלישית שלו (n * n * n)triangularNumber: פונקצייתconstexprשמחשבת את המספר המשולשי ה-n באמצעות הנוסחה n * (n + 1) / 2. במשימה הזו נקרא לה רק בהקשר של חישוב בזמן הידור, ונשמור את התוצאה במשתנהconstexpr.sumOfSquares: פונקצייתconstexprשמקבלת שני מספרים שלמים ומחזירה את סכום הריבועים שלהם (a*a + b*b)
Config.h: צרו מבנה תצורה באמצעות קבועים בזמן הידור.הגדירו מבנה
Configעם בנאיconstexprשמקבל שלושה מספרים שלמים:width,heightו-depth. שמרו אותם כחברים ציבוריים. הוסיפו גם מתודהconstexprבשםvolume()שמחזירה את width * height * depth.מתחת למבנה, צרו קבוע גלובלי מסוג
constexprבשםDEFAULT_CONFIGואתחלו אותו בערכים 10, 20 ו-5.main.cpp: קראו שני מספרים שלמים מהקלט שמייצגים ערכים בזמן ריצה.תחילה, הדגימו חישוב בזמן הידור על ידי יצירת משתני
constexpr:- שמרו את
cube(4)במשתנה constexpr והדפיסו:Cube of 4: [value] - שמרו את
triangularNumber(10)במשתנה constexpr והדפיסו:10th triangular number: [value] - הדפיסו את נפח ברירת המחדל של התצורה:
Default volume: [value]
לאחר מכן, הדגימו שפונקציות
constexprיכולות לפעול גם בזמן ריצה, באמצעות שני ערכי הקלט שלכם:- קראו ל-
cube()עם הקלט הראשון והדפיסו:Cube of [input]: [result] - קראו ל-
sumOfSquares()עם שני ערכי הקלט והדפיסו:Sum of squares: [result]
- שמרו את
לדוגמה, עבור הקלטים 3 ו-4:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 3: 27
Sum of squares: 25עבור הקלטים 5 ו-12:
Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169נסו בעצמכם
#include <iostream>
#include "MathUtils.h"
#include "Config.h"
using namespace std;
int main() {
// קראו שני מספרים שלמים מהקלט
int input1, input2;
cin >> input1;
cin >> input2;
// TODO: הדגימו חישוב בזמן קומפילציה
// צרו משתנה constexpr ששומר את התוצאה של cube(4) והדפיסו: "Cube of 4: [value]"
// TODO: צרו משתנה constexpr ששומר את התוצאה של triangularNumber(10)
// והדפיסו: "10th triangular number: [value]"
// TODO: הדפיסו את עוצמת הקול של תצורת ברירת המחדל: "Default volume: [value]"
// TODO: הדגימו שימוש בפונקציות constexpr בזמן ריצה
// קראו ל־cube() עם input1 והדפיסו: "Cube of [input1]: [result]"
// TODO: קראו ל־sumOfSquares() עם שני ערכי הקלט
// והדפיסו: "Sum of squares: [result]"
return 0;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP
קבצים חיצונייםבנייה וקומפילציה של C++קובצי Header וקובצי מקורמרחבי שמות והיקףמבוא ל-OOP ב-C++מחלקות לעומת אובייקטיםמצביע thisמתודות (פונקציות איבר)תכונות (חברי נתונים)יסודות הבנאים וההורסיםסיכום – מחשבון פשוט4מאפייני מחלקה
חברי מופע לעומת חברים סטטייםגטרים וסטריםפונקציות איבר קבועותמילת המפתח mutableמתודות ומשתנים סטטייםפונקציות ומחלקות ידידותסיכום – מנהל חשבונות בנק7ירושה
ירושה בסיסיתרמות גישה בירושהסדר הקריאות לבנאי ולמפרקדריסת מתודותפונקציות וירטואליות ו־VTableירושה מרובהירושה וירטואליתסיכום – היררכיית עובדים2ניהול זיכרון
זיכרון Stack לעומת Heapמצביעים והפניותזיכרון דינמי (new/delete)מצביעים חכמים ב-C++RAII ב-C++סיכום – מנהל מערכים דינמיים5כימוס
מצייני גישה ב-C++מצייני גישה לעומקהסתרת מידעמבנה לעומת מחלקהמחלקות מקוננות ופנימיותסיכום – מערכת רשומות תלמידים8רב־צורתיות
רב־צורתיות בזמן קומפילציה לעומת בזמן ריצההעמסת פונקציותפונקציות וירטואליות — מבט נוסףפונקציות וירטואליות טהורותמחלקות מופשטותתכנון ממשקים ב־C++המרה דינמית ו־RTTIסיכום — מחשבון צורות3בנאים והורסים
בנאי ברירת מחדלבנאי עם פרמטריםבנאי העתקהבנאי העברהרשימות אתחול בבנאיםבנאים מאציליםמעמיקים בנושא הורסיםכלל השלושה / החמישה / האפססיכום – מחלקת String6העמסת אופרטורים
מבוא להעמסת אופרטוריםהעמסת אופרטורים אריתמטייםהעמסת אופרטורי השוואהאופרטורי זרםהעמסת אופרטור השמההעמסת האופרטורים [] ו־()אופרטורים להמרת טיפוסיםסיכום – מחלקת Matrix9תבניות
תבניות פונקציהתבניות מחלקההתמחות תבניותתבניות ארגומנטים משתנותיסודות SFINAE ותכונות טיפוסיםסיכום – מכל גנרי12תכונות מודרניות ב־C++
סמנטיקת העברה ו־rvaluesהעברה מושלמתביטויי Lambda לעומקstd::function ו־std::bindconstexpr ו־constevalקישור מובנהoptional, variant, anyתרגלו בעצמכם: קומפיילר C++ אונליין