الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

فكر مع الـ template

بدأه C++er في 18 أبريل 2014 · 3 رد · 825 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

القسم نائم، و إليكم سؤال يجعلك تفكر قليلا.

 

هل كتبت أكواد تستخدم الـ template من قبل؟ هل تعتقد انك لديك معرفة جيده بها؟

 

إذا كانت إجابتك بلا لذا مرحبا بك فأنت على وشك انت تتعلم بعض الأمور التى لم تراها من قبل، و إذا كانت إجابتك بنعم فمرحبا بك فى إثراء الموضوع بإسلوب و أفكار لا نعلمها.

 

داخل مكتبة STL للإصدار C++11 توجد الفئه array و هى حاوية لمصفوفه بحجم ثابت يمكن إجراء عمليات عليها و يمكنك الحصول على معلومات عنها من الرابط.

 

المطلوب بسيط هو ان تكتب فئة array تحتوى على دالة واحده فقط copy و هى member function حيث يمكنها نسخ محتويات مصفوفة أخرى حجمها يساوى أو لا يساوى الحجم الذى عرفت به المصفوفه الناسخه. الدالة copy لابد و أن يتم كتابة الكود الخاص بها خارج تعريف الفئه array و إليك مثال:

template<typename T, int N>
struct array
{
	T e[N];

	// place copy function declaration here
};

// place copy function definition here

int main()
{
	array<int, 3> a = { {0, 1, 2} };
	array<int, 10> b = { {0} };

	b.copy(a);
}

الدالة copy لا تعيد قيمة و تنسخ قيمة المصفوفه المدخله إليها بدءا من أول عنصر بالمصفوفه المدخله إلى أول عنصر بالمصفوفة الناسخه بمعنى أول ثلاثة عناصر بالمصفوفه b ستصبح بهم القيم 0 و 1 و 2 و البقيه يتركوا كما هم.

 

دائما سيتم النسخ بعدد العناصر الأقل فإذا كان عدد عناصر a اكبر من عدد عناصر b حينها b ستنسخ من عناصر a ما يساوى عدد عناصرها بدءا من العنصر بالموقع صفر.

 

 

الكود المكتوب لابد من امكانية تنفيذه على مترجم يدعم C++03 و C++11.

 

 

و الله ولي التوفيق

1

مدونتي: C++ Tips and Tricks

#2

ليس لدي وقت لمتابعة التفكير , لذلك كتبت ما وصلت إليه

الحل عندما تكون الأحجام متساوية

أتمنى متابعة الحل

#include <iostream>using std::cout;template<typename T, int N>struct array{    T e[N];    T copy(array<T,N> x);};template<typename T, int N> T array<T,N>::copy(array<T,N> x){    for(int i=0;i<N;i++)        e=x.e;}int main(){    array<int, 10> a = { {0, 1, 2} };    array<int, 10> b = { {0} };    b.copy(a);    cout << a.e[0] << a.e[1];}

والله ولي التوفيق

1
#3

يمكن تعديله ليقيل أي حجم:

 

#include <iostream>
#include <cstring>

template<typename T, int N>
struct array
{
  T e[N];

  // place copy function declaration here
  template <int M>
  inline void copy(const array<T, M>& a);
};

// place copy function definition here
template<typename T, int N>
template <int M>
inline void array<T, N>::copy(const array<T, M>& a)
{
  std::memcpy(e, a.e, sizeof(T) * (M < N ? M : N));
}

int main()
{
  using namespace std;

  array<int, 3> a = { {0, 1, 2} };
  array<int, 10> b = { {0} };

  b.copy(a);

  cout << b.e[0] << endl;
  cout << b.e[1] << endl;
  cout << b.e[2] << endl;
  cout << b.e[4] << endl;
}

 

القوالب تجيب الصداع :wacko:

تم تعديل هذه المشاركة بواسطة Mr.B في 18 أبريل 2014 في 22:09

1
#4

إليكم نسختي:

template<typename T, int N>struct array{	T e[N];	template<int N2>	inline void copy(const array<T, N2>&);};template<typename T, int N>template<int N2>inline void array<T, N>::copy(const array<T, N2>& arr){	const int size = N>N2? N2: N;	for(int i=0; i<size; ++i) e = arr.e;}int main(){	array<int, 3> a = { {0, 1, 2} };	array<int, 10> b = { {0} };	b.copy(a);}

لا تختلف كثيرا عن نسخة الأخ Mr.B. الفكرة تكمن فى كيفية تضمين الدالة copy خارج تعريف الفئة array مع وجود تعريفان للـ template، الدالة copy تصريحها كالتالي:

template<typename T, int N>
struct array
{
	template<int N2>
	inline void copy(const array<T, N2>&);
};

لذا السؤال الذى يتبادر إلى الذهن هو: يوجد ثلاثة متغيرات template للدالة copy فلماذا لا يتم تعريف الدالة copy كالتالي:

template<typename T, int N, int N2>
inline void array<T,N>::copy(const array<T, N2>& arr)
{
}

الدالة copy تسمى template member و يوجد سببان يمنعا هذا التعريف:

 

السبب الأول: الدالة copy هى template member function و هذا يعنى انها تابعه للفئه array و بالتالي تعريف الـ template الذى سيتم مطابقته فى تضمين الدالة copy لابد و أن يطابق التعريف الخاص بالفئه array و لكن انت قمت بإضافة template argument إضافى و هذا يجعل المترجم يصدر أى من الأخطاء التاليه:

 

الخطأ الأول: قد يتم إذا قام المترجم بمطابقة الـ template argument list أثناء مرحلة الـ symantic check و هذا الخطأ قد يكون نصه كالتالي:

invalid argument list for 'array<T,N>::copy' expected 2 found 3.

الخطأ الثاني: قد ينتج من الـ linker إذا تجاهل المترجم التعريف الخاطئ و قام بإستخدام الدالة copy حينها رسالة الخطا قد تبدو كالتالي:

unresolved external symbol 'array<int,10>::copy(array<int, 3> const &)'

و صدقنى فى البرامج الفعليه انت لن تحب أبدا رؤية مثل هذه الرسالة لإن إصلاح الخطأ إن كنت مترمس فإنه يعنى ان لديك خطأ كتابي او انك أختلط الأمر عليك و كتبت شئ فى غير محله و هذا من أصعب الأخطاء التى قد تواجهها مع الـ template بالإضافه لمحاولة فك تشفير رسالة الخطأ.

 

 

السبب الثاني: تخيل معى انك بعد ان انتهيت من كتابة الفئه array قمت بكتابة full specialization لها:

template<typename T, int N> struct array;

template<>
struct array<int, 10>
{
};

عندما تقوم بكتابة full specialization لدالة داخل فئة template فالمترجم يعلم هذا عن طريق إستخدامك للكلمة template مع وجود argument list فارغة، و لكن أنتظر هذا لن يحدث لأن معامل الدالة array يحتاج معامل template و بالتالي تضمين الدالة سيبدو كالتالي:

template<int N2>
void array<int, 10>::copy(const array<int, N2>& arr)
{
}

هنا يتسائل المترجم هل هذا الكود خاص بـ partial specialization أم بـ full specialization لذا سيبدأ بتفيذ الخطوات التى من شأنها إتخاذ القرار كالتالي:

* الدالة copy تنتمى للفئه array و هى فئه template بالتالي تعريف الـ template يخص الفئه array و حيث ان الـ argument list لا تساوى الـ argument list الخاصه بتعريف الفئه array لذا الكود التالي قد ينتمى لـ partial specialization.

* فى هذه المرحلة يتحقق المترجم من معاملات الفئه الموجوده بتضمين الدالة copy فيجد انك قد قمت بتمريرهم كافة لذا يعلم انك قمت بالتصريح عن full specialization و هنا يحدث الإلتباس على المترجم.

 

طبعا قد تتسائل لماذا لم يتحقق من معاملات الدالة copy؟ فإنه إن فعل ستحل المشكلة؟

هذا غير صحيح، تخيل وجود 10 نسخ من الدالة copy منهم إثنان template و البقيه ذوات معاملات عادية حينها على المترجم ان يعيد التطابق من جديد، و لكن انتظر انت تتكلم عن دوال إذا ما رأيك ان ندخل دوال الـ static فى الموضوع أو دوال الـ friend و القائمه طويله و كل منهم له قواعد لمطابقته و لهذا يتم مطابقة معاملات الفئه أولا و إذا نجحت يتم التحقق من تصريح و تضمين الدوال.

 

حل هذه المشاكل هو بكتابة قائمتين للـ template الأولى تخص الفئه و التى عند مطابقتها بنجاح يتم مطابقة القائمه الثانية مع معاملات الدالة.

 

الأن إذا اردت كتابة full specialization او للدالة أو لكلاهما معا فلن تصبح مشكله و سيستطيع المترجم الوصول لها مهما كان عدد الفئات المتداخله لأن لديه وسيلة لمطابقة المعاملات.

 

************************

 

قمت بكتابة toolbox لإستخدامها فى مشاريع خاصه بى، كبديل لفئات الـ stl لأنها صعبة التعامل فى بعض المهمات، فى المرفقات ستجد نسخه كاملة من الفئتين array_t و dynarray_t و اللتان تمثلان نسخه ذات حجم ثابت و أخرى ذات حجم ديناميكي بالإضافة لوجود دوال كثيره للبحث و العدّ، أيضا إمكانية نسخ محتويات ايهم للأخر مهما كان تعريف الـ template المستخدم بشرط ان يكون نوع العناصر واحد و إليك موجز بسيط لمحتويات الملف:

 

syn.hpp: يحتوى على مسميات للأنواع الصحيحه طبقا لحجمها.

 

alloc_t.hpp: هى فئه template تستخدم لحجز و الذاكرة و تغيير حجمها و تحريرها، النسخه الإفتراضية تستخدم malloc, realloc, free و فى مشاريعي لدى نسخه تعمل كـ memory manager و بالنسبه لهذا المشروع فلن تحتاج لتغييرها و يمكنك كتابة نسخه خاصه بك و إستخدامها بسهولة.

 

opr_t.hpp: هى فئه template تحتوى على المعاملات المنطقيه الخاصه بالمقارنه و يم وراثتها فى الفئه التى تريد ان تجعل محتوياتها قابله للمقارنة، فى الفئه الوارثه تحتاج لوجود الدالتين cmp_eq و cmp_lt و الأولى تقارن للتساوى و الثانية تتأكد ان المعامل الاول أقل من الثانى و كلا الدالتين لابد ان يكونا public و static.

 

array_t.hpp: هى فئه template إسمها array_t و تشبه التى كتبناها مع وجود دوال كثير جدا.

array_t.inl: هذا الملف يحتوى على تضمين الفئه array_t.

 

dynarray_t.hpp: هى فئه template إسمها dynarray_t و هى تمثل مصفوفه متغيره الحجم و تحتوى على العديد من الدوال مثل الموجوده بالفئه array_t مع بعض الدوال الأخرى.

dynarray_t.inl: هذا الملف يحتوى على تضمين الفئه dynarray_t.

 

shared.inl: هذا الملف يحتوى على تضمين الدوال المشتركه بين الفئتين array_t و dynarray_t و التى لا يمكن كتابتهم داخل الملف array_t.

 

tools.hpp: هو ملف المكتبه الرئيسي و كل محتوياته تندرج داخل مجال الأسماء tools.

 

tools.zip

 

 

و الله ولي التوفيق

2

مدونتي: C++ Tips and Tricks

مواضيع مشابهة