תנו למהדר לבחור את הטיפוס
בעמוד הקודם ראיתם שלכל משתנה ב-C++ יש טיפוס קבוע: int, double, std::string וכן הלאה. לכתוב את הטיפוס ידנית זה בסדר עבור int count = 0;, אבל זה נהיה רועש כשהטיפוסים מתארכים. מילת המפתח auto (מ-C++11 ואילך) נותנת למהדר להסיק את הטיפוס מהערך שמשימים, כך שכותבים את הערך פעם אחת והמהדר משלים את השאר.
הרעיון המרכזי: auto היא לא טיפוסיות דינמית. לכל משתנה עדיין יש טיפוס קונקרטי אחד, שננעל בזמן הידור. auto רק חוסכת לכם לכתוב אותו.
למה auto שווה את זה
לטיפוסים קצרים וברורים, auto count = 0; ו-int count = 0; קריאים באותה מידה. auto באמת משתלמת עם שמות טיפוסים ארוכים וחוזרים, מהסוג שמקבלים מהמכלים ומהאיטרטורים של הספרייה הסטנדרטית.
השוו את הגרסה המפורטת לגרסה עם auto:
// בלי auto: בפועל הטיפוס נכתב פעמיים
std::vector<std::pair<std::string, int>>::iterator it = scores.begin();
// עם auto: המהדר כבר יודע מה הטיפוס
auto it = scores.begin();
שתיהן מצהירות על בדיוק אותו טיפוס איטרטור. השנייה קלה יותר לקריאה ולא תצא מסנכרון אם בהמשך תחליפו את scores במכל אחר.
הנה זה בתוכנית מלאה:
auto בלולאות for מבוססות טווח
המקום הנפוץ ביותר שבו תפגשו את auto הוא לולאת for מבוססת טווח. כמעט אף פעם לא תרצו לכתוב ידנית את טיפוס האיבר, והאופן שבו כותבים את ה-auto קובע אם מקבלים עותק או הפניה.
שלוש גרסאות שתראו, ומה כל אחת אומרת:
for (auto x : v):xהוא עותק של כל איבר. זול עבורint, בזבזני עבור אובייקטים גדולים.for (auto& x : v):xהוא הפניה; אפשר לשנות את האיברים במקום.for (const auto& x : v):xהוא הפניה לקריאה בלבד. השתמשו בזה כשצריך רק לקרוא.
התוכנית הבאה משנה את המכל דרך auto&:
מלכודת: כתבו for (auto n : nums) (בלי &) בלולאה הזו, וה-n *= 10 ישנה בשקט רק את העותק וישאיר את nums ללא שינוי. המהדר לא יזהיר אתכם: הלולאה פשוט לא עושה שום דבר מועיל.
מה auto מסירה
auto רגילה מסיקה את הטיפוס באותה דרך שפרמטר של פונקציה שמועבר לפי ערך עושה: היא מסירה const ברמה העליונה, הפניות ו-volatile. כלומר auto תמיד נותנת לכם עותק חדש שאפשר לשנות, אלא אם מבקשים אחרת.
אם רוצים לשמור על const או להימנע מהעותק, מוסיפים את המציינים בעצמכם. הדפוס הוא לקשט את auto באותה דרך שהייתם מקשטים כל טיפוס:
כלומר auto מסיקה את טיפוס ה_בסיס_; &, const ו-* הם כפתורים שמוסיפים מעליו. auto הוא הטיפוס, const auto& היא הפניה לקריאה בלבד אליו.
טעויות ומלכודות נפוצות
auto חוסכת הקלדה, לא את הצורך להבין טיפוסים. כמה מלכודות תופסות מתחילים:
חייבים לאתחל. ל-auto אין ממה להסיק בהצהרה ריקה, ולכן זו שגיאת הידור מוחלטת:
auto x; // error: declaration of 'auto x' has no initializer
auto y = 0; // תקין
ליטרלים שלמים הם int, לא double. auto half = 1 / 2; מסיק int ושומר 0, כי 1 / 2 הוא חילוק שלמים עוד לפני ש-auto רואה אותו. הטיפוס נגזר מהערך:
auto מסירה את ההפניה, היזהרו מהפתעות של עותקים. אם פונקציה מחזירה הפניה ואתם תופסים אותה עם auto רגילה, מקבלים עותק, ולפעמים זה באג ביצועים אמיתי בלולאה חמה (העתקה עמוקה של אובייקט גדול בכל איטרציה). השתמשו ב-const auto& כשאתם מתכוונים "להסתכל, לא לקחת".
אל תסתירו את הטיפוס כשהוא חשוב. auto result = compute(); בסדר כשטיפוס ההחזרה של compute ברור מההקשר, אבל אם הקורא צריך לחפש מה result בעצם, כתיבת הטיפוס במפורש יכולה להיות הבחירה הידידותית יותר. auto נועדה לצמצם רעש, לא להסתיר כוונה.
הבא בתור: קבועים ו-const
ראיתם עכשיו ש-auto מסירה בכוונה את const אלא אם מבקשים לשמור עליו, וזה מעלה שאלה מתבקשת: מה const בעצם מבטיח, ומתי כדאי בכלל לסמן ערך כבלתי ניתן לשינוי? העמוד הבא מעמיק ב-const, בביטויים קבועים, ובסיבה ש"להפוך ל-const כברירת מחדל" הוא אחד ההרגלים השימושיים ביותר ב-C++.
שאלות נפוצות
מה עושה מילת המפתח auto ב-C++?
auto אומרת למהדר להסיק את הטיפוס של המשתנה מתוך ערך האתחול שלו. auto x = 5; הופך את x ל-int; auto y = 3.14; הופך את y ל-double. הטיפוס נקבע בזמן הידור: auto היא לא טיפוסיות דינמית, אלא קיצור דרך במקום לכתוב את הטיפוס בעצמכם.
האם auto שומרת על const ועל הפניות ב-C++?
לא. auto רגילה מסירה const ברמה העליונה, הפניות ו-volatile. אם המקור הוא const int& r, אז auto x = r; נותן עותק רגיל מסוג int. כדי לשמור עליהם כותבים אותם במפורש: השתמשו ב-const auto& כדי לקשור הפניה לקריאה בלבד בלי העתקה.
אפשר להצהיר על משתנה עם auto בלי לאתחל אותו?
לא. auto x; היא שגיאת הידור, כי אין ערך אתחול שממנו המהדר יכול להסיק טיפוס. כל משתנה auto חייב לקבל ערך בנקודת ההצהרה.