1.2 - אתחול והסקת טיפוסים - פתרון
פתרון - כמות אינה רשימת איברים¶
התוכנית הבאה בודקת את שתי צורות הבנייה ואת שלוש ההצהרות:
#include <cassert>
#include <cstdio>
#include <type_traits>
#include <vector>
int main() {
std::vector<int> repeated(4, 9);
std::vector<int> values{4, 9};
assert(repeated.size() == 4);
assert(values.size() == 2);
for (int n : repeated) std::printf("%d ", n);
std::puts("");
for (int n : values) std::printf("%d ", n);
std::puts("");
int number = 6;
const int& observed = number;
auto first = observed;
auto& second = number;
decltype((number)) third = number;
static_assert(std::is_same_v<decltype(first), int>);
static_assert(std::is_same_v<decltype(second), int&>);
static_assert(std::is_same_v<decltype(third), int&>);
first = 10;
assert(number == 6);
second = 20;
assert(number == 20);
third = 30;
assert(number == 30);
}
הלולאה הראשונה מדפיסה ארבע פעמים תשע. הלולאה השנייה מדפיסה ארבע ואז תשע. בחירת סוגריים אינה עניין של עיצוב קוד במקרה הזה, מפני ששתי הצורות ניגשות לחוזים שונים של בנאי.
הבדיקות על number מראות שהסקת טיפוס נעשית לכל הצהרה בנפרד. העובדה ש־observed הוא reference אינה מחייבת ש־first יהיה reference. בהצהרה השנייה הוספנו את הסימן במפורש, ובהצהרה השלישית קטגוריית הביטוי בתוך decltype יוצרת טיפוס reference.
סימון המרה מכוונת¶
הקוד הבא הוא קובץ עצמאי לבדיקת הטיפוס:
struct RetryCount {
unsigned value;
explicit RetryCount(unsigned n) : value(n) {}
};
void schedule(RetryCount) {}
int main() {
schedule(RetryCount{3});
// schedule(3);
}
הקריאה השנייה צריכה להידחות אם מסירים את ההערה. הטיפוס unsigned יכול לייצג רק ערכים לא שליליים, אבל אין כאן עדיין בדיקת משמעות של קלט חיצוני. המרת מספר שלילי ל־unsigned לפני בניית האובייקט עלולה ליצור מספר גדול מאוד, ולכן boundary שמקבל קלט חתום צריך לבדוק אותו לפני ההמרה.
החזרת reference מקומי¶
הפונקציה first_value מחזירה int עצמאי. הפונקציה second_value מחזירה int&, בגלל הכללים של decltype((value)). חיי המשתנה המקומי מסתיימים כשהפונקציה חוזרת, וה־reference אינו מאריך אותם.
אפשר לתקן באופן הישיר ביותר:
החזרת הערך אינה דורשת מהקורא להכיר את האחסון המקומי של הפונקציה. גם כתיבת return value; בלי הסוגריים תחת decltype(auto) תשנה כאן את ההסקה ל־int, אבל חתימת int מפורשת מתקשרת את החוזה בצורה ברורה יותר.
היעדר crash בהרצה אינו מכשיר את ה־reference המקורי. ה־bytes של המחסנית עשויים להישאר ללא שינוי לזמן קצר, אבל חיי האובייקט כבר הסתיימו. הפתרון נובע מכללי השפה, ולא ממה שהדיבאגר הספיק להציג.