בעיה בtemplate עם class

liran22

New member
בעיה בtemplate עם class

שלום יש לי בעיה מסוימת ב C++ שאני מנסה לכתובtemplate class במצעות קובצי הידר וקובץ CPP לישום הפונקציות ההגדרה של הCLASS בקובץ ההידר ניראת כך הגדרת הtemplate מעל הגדרת ה class ותכונה פרטית של העצם היא template שאר הגדרות הפונקציות רגילות קובץ הישיום נראה כך: מעל כול ישיום של פונקציה מוגדר template ובהגדרת הפונקציה בצמוד לשיכות :: רשום <T> בMAIN אני מגדיר עצם עם הצמדה <int> הבעיה שלי היא שזה עובר קופלציה אבל לא עובק קישור משהוא יודע איך צריך לכתוב את ההגדרות תודה מראש.
 

liran22

New member
אני חייב לממש גם הדר וגם cpp

השאלה היא איך אני עושה את זה?
 

ברנדל

New member
כמו שאמרתי

שים גם את המימוש וגם את ה declaration באותו קובץ header, ולאחר מכן תעשה לקובץ הזה include ל cpp שבו אתה רוצה להשתמש במחלקה הזו.
 

liran22

New member
אני אנסה להסביר שוב

אני צריך שיהיו לי קובץ הידר רק עם הגדרות של class template וקובץ CPP שבו יש רק את המימושים של אותה מחלקת template בצורה שבכול באותו קובץ זה עובד.
 

annefan

New member
אתה לא יכול

יש רק קומפיילר אחד שמממש את המילה השמורה export שעושה מה שאתה רוצה, כלומר "מיצאת" את ה-template, כך שקבצי cpp אחרים יוכלו לראות אותן. בהנחה שאתה לא עובד עם Comeau בתור קומפיילר, לא תוכל לעשות את מה שאתה רוצה. חבל על הזמן שלך.
 

liran22

New member
אני לא מבין תוכל להסביר יותר בהרחבה

שאני יוצר class רגיל אני יוצר שני קבצים אחד הידר שם אני רושם את התכנות הפרטיות של הclass ואת החתימות של הפונקציות. ובקובץ השני שהוא cpp אני ממש את הפונקציות של הclass כמובן תוך שימוש באופרטור שיכות:):) אז תוכל להסביר למה אני לא יכול לעשות אותו דבר רק עם template ?
 

annefan

New member
למה יש לי תחושה שזו כבר פעם רביעית?

בהעברת תוכנית מקוד מקור לקובץ ריצה יש כמה שלבים. העיקריים שבהם לעניין הזה הם הפרהפרוססור, קומפילציה והלינק - קדם-עיבוד, הידור וקישור בעברית. שלב ההידור מתבצע על כל "יחידת קומפילציה" - Compilation Unit - בנפרד. "יחידת קומפילציה" היא, למעשה, קובץ מקור לאחר ה-pre-processor, כלומר לאחר שכל הקבצים שהכללת ב-#include הוכנסו לתוך הקובץ, וכל המקרואים שהגדרת ב-#define הומרו לערכים שלהם, ולאחר שכל הקטעים שהיו בתוך קומפילציה מותנית (#ifdef וכד') הוצאו מהקובץ. הקובץ הזה (שניתן לייצר אותו עם gcc -E, או עם ה-flag של MSDEV - /P /E) הוא מה שעובר לקומפיילר. בשלב הזה אין לקומפיילר (ואני מתכוון לחלק שמבצע הידור בלבד) שום מושג מה יש בקבצי מקור אחרים. עכשיו, הקומפיילר מקמפל את הקובץ. משתנים ופונקציות שהוא מוצא בקובץ עצמו, הוא מכוון פנימית. מה שהוא לא מוצא, הוא משאיר מקום פנוי בטבלה, ומחכה ללינקר שישלים. דוגמא:
// pr_sq.h void print_square(int i); // pr_sq.cpp #include <iostream> #include "pr_sq.h" void print_square(int i) { std::cout << i * i << std::endl; } // main.cpp #include "pr_sq.h" int i; // It's global in purpose int main() { i = 3; print_square(i); return 0; }​
מה קורה פה? ה-Pre-Processor עובר על sq_pr.cpp, מכניס (ממש מכניס!) את ה-h ואת iostream לתוך הקובץ. הקומפיילר מקמפל ויוצר קובץ אוביקט. ה-Pre-Processor עובר על main.cpp ויוצר קובץ אוביקט נוסף. אם תשים לב, i אינו מוגדר ב-scope של main ולכן, הקומפיילר יחפש אותו קודם כל בקובץ, ימצא אותו ב-scope הגלובלי וימקם אותו בטבלת ה-symbols שלו. מצד שני הוא יחפש את void print_square(int). הוא ימצא את ההצהרה בגלל שהכללת את pr_sq.h אבל לא את הסימבול עצמו. לכן, הוא לא יעצור בשלב הקומפילציה עם טעות, ויחכה ללינקר שישלים את החסר. הלינקר יקשר את שני קבצי האוביקטים הנ"ל והתוכנית תרוץ והכל יופי. ועכשיו ל-templates. כשמגדירים פונקצית template (או מחלקה, זה לא חשוב במקרה הזה), הקומפיילר מייצר instances שלה לפי הצורך. כלומר, אם אתה מגדיר:
// pr_sq.h template <tpyename T> void print_square(T t); // pr_sq.cpp #include <iostream> #include "pr_sq.h" template <typename T> void print_square(T t) { std::cout << t * t << std::endl; } // main.cpp #include "pr_sq.h" int main() { int i = 3; print_square(i); double d = 3.3; print_square(d); return 0; }​
אתה מצפה שהקומפיילר ייצור לך פונקציות מתאימות. אבל, לקומפיילר אין מושג מה ליצור! למה? בקובץ שמגדיר את הפונקציה, לקומפיילר אין שום מוטיבציה ליצור instances כי אין שום שימוש בפונקציה הזו (ואתה לא יכול לצפות שהוא ינחש לאלו סוגי טיפוסים הוא אמור ליצור). מצד שני, בקובץ שמשתמש בפונקציה, הקומפיילר מחפש הגדרה של print_square שמקבלת int ומחזירה void ולא מוצא. לכן, בשני המקומות הוא משאיר את זה ללינקר, שכמובן גם לא מוצא בשום קובץ symbol מתאים לפונצקיה הזו, ולכן אתה נשאר עם undefined symbol והתוכנית נופלת. מקווה שהבעיה ברורה. פתרונות: 1. להכניס גם את הגדרת ה-template לקובץ h. כיוון שקובץ כזה מוכלל ממש ב-cpp, הקומפיילר כבר יראה הכל בזמן קומפילציה וייצור פונקתיות מתאימות ל-int ול-double. 2. ליצור שימוש מלאכותי בקובץ cpp של הגדרת הפונקציה, כלומר אחרי הגדרת הופנקציה, להוסיף:
template void print_square<int>(int t); template void print_square<double>(double t);​
זה מה שנקרא explicit instansiation. היתרון, זה ממשיך לשמור על הפרדה בין הכרזה למימוש-הגדרה. החסרון, אתה צריך לעשות זאת לכל סוג משתנה שתשתמש בו אי-פעם, דבר שדי מנוגד לכל הרעיון של templates. 3. לחכות קצת עד שיותר קומפיילרים-לינקרים יתמכו במילה השמורה export, שתעשה את הקסם הזה לבד. הפתרון הפופולרי ביותר הוא 1. אם יש שאלות תשאל.
 

ברנדל

New member
תגיד

גם לי לא מובן משהו: א: "בקובץ שמשתמש בפונקציה, הקומפיילר מחפש הגדרה של print_square שמקבלת int ומחזירה void ולא מוצא. לכן, בשני המקומות הוא משאיר את זה ללינקר" למה הוא משאיר את זה ללינקר, בד"כ כשיש קריאה לפונקציה, שאין לה הצהרה, הקומפיילר צועק. ב. יש סיבה מיוחדת שאתה משתמש ב typename ולא ב <class T>?
 

vinney

Well-known member
אין בעיה להשתמש ב Typename

בהגדרת ה template זה לא משנה, ההבדל בין השניים הוא במקום אחר.
 

vinney

Well-known member
מי אמר שלא? בטח שיכולה

בדיוק באותה צורה כמו כל class אחר, למה שtemplate יעמוד בדרכך? רק צריך לזכור לרשום את ה template לפני כל פונקציה ממומשת, אם היא ממומשת מחוץ להגדרת הclass (שזה לפי מה שהבנתי מה שחסר לך). זה נראה בערך כך (ותסלחו לי על היישור): template <class T> class cT { T something(T); }; template <class T> T cT::something(T) { whatever the code is }
 

vinney

Well-known member
אבל צדק מי שכתב

שזה צריך להיות בקובץ #include, אחרת זה באמת לא יעבור קישור. אתה יכול לשים מימוש של template בcpp, אם זאת הדרישה, אבל אתה חייב לעשות #include לקובץ הזה לפני השימוש בtemplate.
 
למעלה