Hello our valued visitor, We present you the best web solutions and high quality graphic designs with a lot of features. just login to your account and enjoy ...

<none>

Hello our valued visitor, We present you the best web solutions and high quality graphic designs with a lot of features. just login to your account and enjoy ...

CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
6 + 10 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.

أخبار تكنلوجيا

رقم الخبر عنوان الخبر التفاصيل
28,980 آيسر تكشف عن الحاسوب المحمول المتحول Spin 7 بمعالج كوالكوم ودعم الجيل الخامس

أعلنت شركة آيسر اليوم عن الحاسوب المحمول المتحول Spin 7 والذي يأتي مع معالج كوالكوم Snapdragon 8cx Gen 2 ليكون من أوائل الأجهزة التي تعمل بهذا المعالج المطور عن الجيل السابق، وهو ما ساعد الجهاز على تقديم دعم تقنية الجيل الخامس بفضل الرقاقة المدمجة بالمعالج.

وبتقديمه دعم الجيل الخامس، فإن حاسوب آيسر الجديد سيوفر الدعم لتقنيات الاتصال mmWave و sub-6 للحصول على السرعة المناسبة.

يتميز الحاسوب المحمول المتحول الجديد Spin 7 بتصميم نحيف للغاية وحواف صغيرة مع قدرة على الاستدارة 360 درجة مع يساعد في استخدامه كحاسوب محمول أو جهاز لوحي في نفس الوقت، خاصة وأنه يضم شاشة بقياس 14 بوصة من نوع IPS بدقة FHD تدعم خاصية اللمس وكذلك القلم الذكي للحصول على تجربة أفضل. كما أن الشاشة محمية بطبقة غوريلا غلاس لتمنح صلابة أكثر للمستخدمين.

ويضيف الجهاز الجديد قدرات صوتية عالية بفضل تقنية Dolby Audio، ويقدم بطارية تستمر حتى 24 على شحنة واحدة وفقًا للشركة.

سيعمل حاسوب آيسر المحمول بنظام تشغيل ويندوز 10 من مايكروسوفت ما يعني قدرات أعلى من الأجهزة بمعالجات كوالكوم بنظام كروم.

لم تنشر الشركة الكثير من التفاصيل الأخرى حول الجهاز على موقعها الإلكتروني، ومن المنتظر معرفة المزيد وقت الإطلاق.

بدورها، لم تكشف الشركة التايوانية عن موعد إتاحة هذا الجهاز في السوق ولا حتى السعر الذي سيأتي معه عند إطلاقه.

المصدر:

Acer

التدوينة آيسر تكشف عن الحاسوب المحمول المتحول Spin 7 بمعالج كوالكوم ودعم الجيل الخامس ظهرت أولاً على عالم التقنية.

28,979 آبل تتيح لمطوري التطبيقات منح كوبونات التخفيضات على الاشتراكات

قريباً ستمكن شركة آبل مطوري التطبيقات الخاصة بأجهزة iOS سواء شركات أو أفراد من منح المستخدمين كوبونات التخفيضات أو حتى الحصول على اشتراكات مجانية كنوع من الحملة التسويقية.

وأعلنت آبل عن دعمها لتوزيع كوبونات تستعمل مرة واحدة لكل كوبون سواء رقمياً أو فيزيائياً وذلك بغرض إما كسب مشتركين جدد، أو الحفاظ على المشتركين الحاليين وردعهم عن إلغاء اشتراكهم.

ستوفر آبل هذا الدعم للمطورين في وقت لاحق من هذا العام وسيكون فقط لمستخدمي iOS 14 و iPadOS 14.

هذه ليست المرة الأولى التي توفر فيها آبل شيء مشابه، سبق لها أن سمحت للمطورين بتقديم أسعار مخصومة أقل من المعتاد للمشتركين للمرة الأولى في خدماتهم وذلك خاصة عند الاشتراك لفترة طويلة مثل سنة.

لكن مع الشكل الجديد لهذه الخاصية عبر كوبونات الخصم والتخفيضات أو حتى الاشتراك المجاني، فإن آبل تتيح تحكم أكبر للمطورين.

وتوفر آبل عدة طرق لتوزيع الكوبونات مثل إرسالها للمستخدم بعد شراء منتج ما عبر البريد الإلكتروني، ويمكن إدخال الكوبون للاستفادة منه سواء من تطبيق متجر التطبيقات أو عبر الويب أو مباشرة ضمن التطبيق الخاص به.

هذا يعني يمكن لخدمات الاشتراكات مثل نتفلكس وسبوتيفاي وغيرها أن توفر لمستخدميها كوبونات تخصم جزء من الرسوم عند الاشتراك لفترة طويلة، أو تقدم اشتراك مجاني لمدة محدودة كنوع من تجربة الخدمة، ما عدا ما كانت توفره أساساً كفترة تجريبية مجانية.

المصدر:

Apple

التدوينة آبل تتيح لمطوري التطبيقات منح كوبونات التخفيضات على الاشتراكات ظهرت أولاً على عالم التقنية.

28,978 Shortcut Manager لإنشاء اختصارات للتطبيقات والأنشطة على شاشة أندرويد

بدون مُقدّمات Shortcut Manager هو مجرد تطبيق يتيح لك إنشاء اختصارات ملائمة لشاشتك الرئيسية، بحيث لا يمكنك فقط إنشاء الاختصارات الخاصة بك وتخصيصها لجميع تطبيقاتك العادية، ولكن يمكنك أيضًا إنشاء اختصارات للأنشطة خارج التطبيق، لذلك إذا كنت بحاجة إلى مزيد من التخصيص للتطبيقات على شاشتك الرئيسية، أو كنت ترغب ببساطة في إنشاء اختصارات لوظائف تطبيقات معينة، فإن Shortcut Manager هو التطبيق الذي تبحث عنه.

بعبارة أخرى، مع تطبيق Shortcut Manager ستتمكّن من إنشاء اختصارات للتطبيقات والأنشطة والاختصارات الأخرى لنظام أندرويد، وهو بالمناسبة أيضًا تطبيق صانع لأيقونات التطبيقات، فضلًا على أنه مبدّل للتطبيقات على شاشتك الرئيسية.

فعلى سبيل المثال، يمكنك انشاء اختصار للاتصال المباشر بجهة اتصال، أو لإرسال رسالة نصية قصيرة، فضلًا عن وضع اختصارات للبلوتوث والواي فاي والموقع وغير ذلك الكثير.

أخيرًا، يتوفّر تطبيق Shortcut Manager بنسختين الأولى مجانية والثانية متاحة للتحميل بسعر 0.99$، وطبعًا مع الأخيرة ستحصل على جميع قدرات التطبيق، عكس النسخة المجانية المحدودة.

تحميل النسخة المجانية من تطبيق ShortcutManager.

تحميل النسخة المدفوعة من تطبيق ShortcutManager.

التدوينة Shortcut Manager لإنشاء اختصارات للتطبيقات والأنشطة على شاشة أندرويد ظهرت أولاً على عالم التقنية.

28,977 يُحوّل تطبيق CocoFax الجديد هاتفك إلى جهاز فاكس مع تشفير بياناتك لأقصى حد

هناك الكثير من التطبيقات التي يمكن الاستعانة بها بدلًا من شراء الأجهزة، فعلى سبيل المثال، هناك تطبيقات الماسح الضوئي، وتطبيقات مكالمات الفيديو، وحتى تطبيقات تحويل الهاتف إلى موجّه لاسلكي والكثير من المجالات الأخرى، CocoFax أحدث هذه التطبيقات، والذي بواسطته ستُحوّل هاتفك الأندرويد إلى فاكس.

بالتالي يجعل تطبيق CocoFax إرسال الملفات عبر الفاكس بين الزملاء أمرًا سهلاً تمامًا كإرسال بريد إلكتروني واستلامه، ومعه ستتمكن من البدء بالإرسال إلى 189 دولة في العالم، ويمكنك حتى الحصول على رقم فاكس مجاني وإرسال ما يصل إلى 10 صفحات، وطبعًا هناك الاشتراكات التي توفّر لك إرسال العشرات من الصفحات.

أما فيما يخص إمكانية الاستخدام، فما عليك سوى الاشتراك في نسخة تجريبية مجانية مدتها 30 يومًا لتلقي رقم الفاكس المجاني الخاص بك، وهذه النسخة تختلف عن النسخة المجانية المحدودة، ثم باستخدام التطبيق، ابدأ رسالة بريد جديدة وأضف ملفاتك واكتب رمز بلد المستلم + رقم الفاكس + @ cocofax.net في الحقل “إلى”، ثم أرسلها، على أن تلقي رسائل الفاكس مباشرة إلى صندوق الوارد الخاص بك، مما يلغي الحاجة إلى شراء أو صيانة جهاز فاكس ضخم عفا عليه الزمن.

ومن مميزات التطبيق، ميزة التخزين السحابي الغير محدودة، والتشفير التام، بالتالي يمكنك عرض جميع الفاكسات التي تم تبادلها من قبل في السحابة في أي وقت تشاء، أخيرًا، تطبيق CocoFax متاح للتحميل مجّانًا مع وجود اشتراكات لاستخدامه بكامل طاقته، ويمكنك تحميله عبر الرابط بالأسفل.

تحميل تطبيق CocoFax على أندرويد.

التدوينة يُحوّل تطبيق CocoFax الجديد هاتفك إلى جهاز فاكس مع تشفير بياناتك لأقصى حد ظهرت أولاً على عالم التقنية.

28,976 أقل من عام على تعيينه .. استقالة المدير التنفيذي لـ HTC لأسباب عائلية شخصية

أعلنت شركة HTC عن استقالة رئيسها ومديرها التنفيذي الحالي Yves Maitre وذلك لما أسماها “أسباب شخصية” بالرغم من تعيينه قبل أقل من سنة.

وأصبحت رئيسة مجلس الإدارة والشريكة المؤسس Cher Wang مرة أخرى كمدير تنفيذي للشركة حتى يتم تعيين شخص آخر.

وبحسب البيان الرسمي من HTC، فإن مديرها التنفيذي الفرنسي قضى 11 شهراً بعيداً عن أسرته المقيمة في أوروبا بسبب قيود السفر الدولية التي فرضت نتيجة جائحة كورونا، ولهذا تقدم بطلب استقالته.

خلال فترة إدارة Maitre، ركّزت الشركة على تسريع جهودها وتعزيز تواجدها في سوق منتجات وخدمات اولاقع الافتراضي حيث تم توسيع عائلة منتجات Vive Cosmos وتعمل الشركة على الجيل الجديد بتصميم مدمج أكثر.

كما أطلقت الشركة حزمة Vive XR Suite للتركيز على خدمات ومنتجات الواقع الافتراضي الموجهة للشركات، حتى أن HTC أطلقت أول هاتف لها يدعم شبكات الجيل الخامس وهو U20.

بالرغم من كل هذا النشاط خلال أقل من عام، لم تصل HTC بعد إلى الربحية، وبلغت خسائرها الربع الماضي نحو 62 مليون دولار وهي أكبر من خسائر الربع السابق 57 مليون دولار، بالرغم من جولتي تسريع للعمالة وخفض للتكاليف.

المصدر:

Engadget

التدوينة أقل من عام على تعيينه .. استقالة المدير التنفيذي لـ HTC لأسباب عائلية شخصية ظهرت أولاً على عالم التقنية.

28,940 جديد "تويتر".. التسهيل على ذوي الاحتياجات الخاصة

بعد تلقيها انتقادات لعدم تضمين أدوات الوصول لتسميات توضيحية مكتوبة لميزتها الصوتية الجديدة التي أطلقت مؤخراً، أعلنت منصة "تويتر" عن دفعة جديدة لتحسين خيارات إمكانية الوصول للتغريدات واستخدامها من جميع المستخدمين المحتملين بمن فيهم ذوو الاحتياجات الخاصة.

وقالت في تدوينة: نحن نعلم أننا بحاجة إلى بذل المزيد من الجهد لجعل خدمتنا متاحة وسنفعل ذلك.

وعندما اختبرت تويتر ميزة جديدة تتيح للمستخدمين مشاركة المقاطع الصوتية مع التغريدات، تلقت انتقادات لعدم تضمين أدوات الوصول مثل التسميات التوضيحية المكتوبة.

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

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

إلى ذلك، أطلقت تويتر فريقين جديدين: فريق مركز التميز لإمكانية الوصول (ACE) وفريق إمكانية الوصول إلى التجربة (EAT).

ومن المفترض أن يجعل فريق مركز التميز لإمكانية الوصول (ACE) جوانب تويتر، مثل المساحات المكتبية والتسويق والمعايير القانونية والسياسات، أكثر سهولة.

بينما سيعمل فريق إمكانية الوصول إلى التجربة (EAT) على تسهيل الوصول إلى المنتجات والميزات الجديدة والحالية.

هذا ويعمل موقع الطائر الأزرق، على إضافة تسميات توضيحية تلقائية إلى الصوت والفيديو بحلول أوائل عام 2021، مما يضع أساسًا لخارطة طريق طويلة المدى تستثمر على نطاق واسع في إمكانية الوصول إلى الوسائط عبر تويتر.

كما قال: تعني خدمة المحادثة العامة اتخاذ خطوات مستمرة لجعل الوصول إلى تويتر أكثر سهولة، ويجب أن يشمل ذلك الأشخاص ذوي الإعاقة، وقد جعلنا اختبار التغريدات الصوتية ندرك حجم العمل الذي ما زلنا بحاجة إلى القيام به كشركة.

وأضاف "التزمنا بجعل تويتر أكثر شمولاً لمجتمع ذوي الاحتياجات الخاصة، مع إيجاد فرق خاصة للتركيز على إمكانية وصول أكبر عبر جميع منتجاتنا".

هذا ودخلت تويتر في شراكة مع مجموعات خارجية، وستجمع على مدار الأشهر القادمة التعليقات من الأشخاص ذوي الإعاقة عبر المقابلات والاستطلاعات، إلى جانب إجراء دراسات قابلية الاستخدام عن بُعد للنماذج الأولية الجديدة.

في المقابل، أضافت منصة فيسبوك التسميات التوضيحية التلقائية في عام 2016، بينما أضافت لينكدإن علامات نص بديل تلقائي العام الماضي، ويبدو أن تويتر متأخر قليلاً في هذا الصدد.

28,939 بعد اليوم الأسود.. حساب شهير ضحية اختراق آخر في تويتر

بعد مرور حوالي شهرين على "الأربعاء الأسود" الذي ضرب تويتر، وقع اسم جديد شهير في فخ الاختراق. فقد أكد موقع توتير الخميس أنه تم اختراق حساب رئيس وزراء الهند ناريندرا مودي.

وتماما كما حصل مع عشرات المشاهير في ما عرف بـ "الأربعاء الأسود"، نشر المخترقون سلسلة تغريدات، حذفت لاحقا، تطلب من متابعي مودي التبرع بالعملة المشفرة، لصالح صندوق الإغاثة الوطنية

وتعليقا على تكرار تلك المشكلة الخطيرة، اكتفى تويتر بالقول إنه على علم بهذا النشاط وقد اتخذ خطوات لتأمين الحساب الذي تعرض للاختراق.

" لا علم بتأثر حسابات أخرى"

كما أوضحت متحدثة باسم تويتر في رسالة بالبريد الإلكتروني قائلة "نعكف على تقصي الوضع. في الوقت الحالي، لا علم لنا بتأثر حسابات أخرى".

يذكر أنه في تموز/يوليو، تعرضت حسابات عشرات الشخصيات على تويتر، بمن فيهم بيل غيتس وإيلون ماسك وباراك أوباما للاختراق.

وأرسل القراصنة من تلك الحسابات رسائل خادعة دعوا فيها المتابعين إلى إرسال عملات "بتكوين" افتراضية والحصول في المقابل على ضعف المبلغ.

وأوضحت المنصة الاجتماعية في حينه أن القراصنة الإلكترونيين استهدفوا 130 حسابا ونجحوا في اختراق 45 منها نتيجة "استعمال أدوات متوافرة فقط لفرق الدعم الداخلي" في الشركة.

إلا أن تويتر أشار اليوم إلى أنه لم يتمكن من إثبات وجود صلة بين اختراق حساب رئيس الوزراء الهندي والحسابات التي تعرضت للقرصنة في منتصف تموز/يوليو.

28,916 الدرس 42: القوالب Templates في Cpp

صار بالإمكان قولبة الأصناف والدوالّ والمتغيّرات في لغة ++C منذ C++‎ 14، والقالب هو شيفرة لها بعض المعاملات الحرّة (free parameters) التي ستُستبدَل فيما بعد بأصناف أو دوال أو متغيّرات حقيقية عند تحديد تلك المعاملات.

وقد تكون المعاملات أنواعًا أو قيمًا أو قوالب بحد ذاتها، ومن أمثلة القوالب: المتجهات (‎std::vector‎)، التي تصبح أنواع حاويات حقيقية عند تحديد نوع العنصر، كما في ‎std::vector<int>‎.

قالب صنف أولي Basic Class Template

الفكرة الأساسية لقالب الصنف هي أنّ مُعامل القالب سيُستبدَل بنوع معيّن في وقت التصريف، ونتيجة لذلك يمكن استخدام نفس الصنف مع عدّة أنواع. يحدّد المستخدمُ النوعَ الذي سيُستخدَم عند التصريح عن متغيّر من ذلك الصنف، ولدينا ثلاثة أمثلة على ذلك:

#include <iostream> using std::cout; template < typename T > // صنف بسيط لاحتواء عدد من أيّ نوع class Number { public: void setNum(T n); // ضبط حقل الصنف عند العدد المُعطى T plus1() const; // "follower" يعيد حقل الصنف private: T num; // حقل من الصنف }; template < typename T > // ضبط حقل الصنف عند العدد المُعطى void Number<T>::setNum(T n) { num = n; } template < typename T > // "follower" إعادة T Number<T>::plus1() const { return num + 1; } int main() { Number<int> anInt; // (في الصنف T ستستبدل int) إجراء اختبار مع عدد صحيح anInt.setNum(1); cout << "My integer + 1 is " << anInt.plus1() << "\n"; // 2 يطبع Number<double> aDouble; // double الاختبار بعدد من النوع aDouble.setNum(3.1415926535897); cout << "My double + 1 is " << aDouble.plus1() << "\n"; // يطبع 4.14159 Number<float> aFloat; // الاختبار بعدد عشري aFloat.setNum(1.4); cout << "My float + 1 is " << aFloat.plus1() << "\n"; // يطبع 2.4 return 0; } قوالب الدوال Function Templates

يمكن تطبيق القولبة على الدوالّ (والهياكل التقليدية الأخرى)، انظر المثال التالي حيث تمثل T نوعًا مجهولًا، ويكون كلا الوسيطين من نفس النوع:

template < typename T> void printSum(T add1, T add2) { std::cout << (add1 + add2) << std::endl; }

يمكن استخدامها بنفس الطريقة التي تستخدم بها قوالب الهياكل (structure templates).

printSum<int> (4, 5); printSum<float> (4.5 f, 8.9 f);

يُستخدَم وسيط القالب في كلتا الحالتين لاستبدال أنواع المعاملات؛ وستعمل النتيجة بشكل مشابه لدوالّ C++‎ العادية، فإذا لم تتطابق المعاملات مع نوع القالب، فإنّ المٌصرّف سيطبّق التحويلات القياسية.

إحدى الخصائص الإضافية لدوالّ القوالب -على عكس أصناف القوالب- هي أنّ المُصرّف يمكنه استنتاج معاملات القالب بناءً على المعاملات المُمرّرة إلى الدالّة.

في الحالة التالية يكون كلا المعاملين من نوع int، وذلك يتيح للمصرف استنباط النوع، وتكون T مساوية لـ int:

printSum(4, 5);

في هذه الحالة يكون المعاملان من نوعين مختلفين، ويعجز المصرف عن استنتاج نوع T بسبب وجود تناقضات، ونتيجة لهذا يحدث خطأ في التصريف.

printSum(5.0, 4);

تتيح لنا هذه الميزة تبسيط الشيفرة عند الجمع بين بنيات ودوالّ القالب. يوجد نمط شائع في المكتبة القياسية يسمح لنا بجعل ‎template structure X‎ تستخدم دالّة مساعدة ‎make_X()‎. انظر المثال التالي الذي يوضح كيف يبدو نمط make_X:

  1. هيكل قالب مع نوع قالب واحد أو أكثر:
template < typename T1, typename T2> struct MyPair { T1 first; T2 second; };
  1. دالة make لها نوع لكل معاملات القوالب في هيكل القالب
template < typename T1, typename T2> MyPair<T1, T2> make_MyPair(T1 t1, T2 t2) { return MyPair<T1, T2> { t1, t2 }; }

كيف يساعد هذا؟ انظر ما يلي حيث يكون val1 و val2 من نفس النوع:

auto val1 = MyPair<int, float> { 5, 8.7 }; // إنشاء الكائن يعرّف الأنواع صراحة auto val2 = make_MyPair(5, 8.7); // إنشاء الكائن باستخدام أنواع المعاملات

ملاحظة: لم يُصمّم هذا لاختصار الشيفرة وإنّما صُمِّم لجعلها أكثر متانة إذ يسمح بتغيير الأنواع عن طريق تغيير الشيفرَة في مكان واحد وليس في مواقع متعدّدة.

قوالب التعبير Expression templates

قوالب التعابير هي تقنية تحسين (optimization) في وقت التصريف تُستخدم في الغالب في الحوسبة العلمية (scientific computing)، والغرض الرئيسي منها هو تجنّب إهدار الوقت وتحسين الحسابات باستخدام مسار واحد -عادةً عند إجراء عمليات على المجاميع العددية-.

في البداية، تُقسَّم قوالب التعابير من أجل التحايل على الفوارق الناجمة عن التحميل الزائد عند تنفيذ أنواع المصفوفات (‎Array‎) أو المصفوفات المتعددة (‎Matrix‎).

ويجب أن تفهم الهدف من قوالب التعبير أولًا قبل الغوص فيها، انظر الصنف Matrix الوارد في المثال أدناه:

template < typename T, std::size_t COL, std::size_t ROW> class Matrix { public: using value_type = T; Matrix(): values(COL *ROW) {} static size_t cols() { return COL; } static size_t rows() { return ROW; } const T &operator()(size_t x, size_t y) const { return values[y *COL + x]; } T &operator()(size_t x, size_t y) { return values[y *COL + x]; } private: std::vector<T> values; }; template < typename T, std::size_t COL, std::size_t ROW> Matrix<T, COL, ROW> operator+(const Matrix<T, COL, ROW> &lhs, const Matrix<T, COL, ROW> &rhs) { Matrix<T, COL, ROW> result; for (size_t y = 0; y != lhs.rows(); ++y) { for (size_t x = 0; x != lhs.cols(); ++x) { result(x, y) = lhs(x, y) + rhs(x, y); } } return result; }

يمكنك الآن كتابة تعبيرات Matrix على النحو:

const std::size_t cols = 2000; const std::size_t rows = 1000; Matrix<double, cols, rows> a, b, c; // a, b &c هيئ for (std::size_t y = 0; y != rows; ++y) { for (std::size_t x = 0; x != cols; ++x) { a(x, y) = 1.0; b(x, y) = 2.0; c(x, y) = 3.0; } } Matrix<double, cols, rows> d = a + b + c; // d(x, y) = 6

كما هو مُوضّح أعلاه، فتستطيع توفير صيغة تحاكي الصيغة الرياضية المعتادة للمصفوفات عبر التحميل الزائد للعامل ‎operator+()‎، لكن التنفيذ السابق غير فعّال مقارنةً بالإصدارات المكافئة "المصنوعة يدويًا".

ولفهم السبب، عليك مراعاة ما يحدث عند كتابة تعبير مثل ‎Matrix d = a + b‎ + c‎، يُنشر هذا التعبير في الواقع إلى التعبير ‎((a + b) + c)‎، أو ‎operator+(operator+(a, b), c)‎، أي تُنفّذ الحلقة الموجودة داخل operator+()‎ مرّتين، بينما كان من الممكن إجراؤها مرّة واحدة. هذا يؤدّي أيضًا إلى إنشاء كائنين مؤقّتين، مما يضعف الأداء أكثر. ويبدو أنّ المرونة التي حصلنا عليها والناتجة عن استخدام صياغة قريبة للصياغة المعمول بها في الرياضيات كانت على حساب الأداء.

على سبيل المثال، تستطيع تنفيذ تجميع مصفوفةٍ بأسلوب أفضل بدون التحميل الزائد للعامل، وباستخدام مسار تمرير واحد:

template < typename T, std::size_t COL, std::size_t ROW> Matrix<T, COL, ROW> add3(const Matrix<T, COL, ROW> &a, const Matrix<T, COL, ROW> &b, const Matrix<T, COL, ROW> &c) { Matrix<T, COL, ROW> result; for (size_t y = 0; y != ROW; ++y) { for (size_t x = 0; x != COL; ++x) { result(x, y) = a(x, y) + b(x, y) + c(x, y); } } return result; }

لكنّ المثال السابق له عيوبه، لأنّه ينشئ واجهة أكثر تعقيدًا للصنف Matrix، إذ سيكون عليك اعتبار توابع من قبيل: ‎Matrix::add2()‎ و ‎Matrix::AddMultiply()‎. بدلاً من ذلك، دعنا نرجع خطوة إلى الوراء ونرى كيف يمكننا تكييف التحميل الزائد للمُعامل لأجل تحسين الأداء.

تنبع المشكلة من حقيقة أنّ التعبير ‎Matrix d = a + b + c‎ يُقيَّم قبل إنشاء شجرة التعبير بأكملها، أي ما نريده حقًا هو تقييم ‎a + b + c‎ مرّة واحدة، وفقط عندما تحتاج إلى إسناد التعبير الناتج إلى ‎d‎.

وتلك هي الفكرة الأساسية وراء قوالب التعبير: بدلاً من أن يُقيّم المعامل ‎operator+()‎ نتيجة إضافة نسختين من Matrix على الفور، فسيُعيد "قالبَ تعبير" لتقييمه مستقبلًا بعد الانتهاء من بناء شجرة التعبير بأكملها.

على سبيل المثال، فيما يلي تنفيذ ممكن لقالب تعبير يتوافق مع جمع عنصرين من نوعين مختلفين:

template < typename LHS, typename RHS> class MatrixSum { public: using value_type = typename LHS::value_type; MatrixSum(const LHS &lhs, const RHS &rhs): rhs(rhs), lhs(lhs) {} value_type operator()(int x, int y) const { return lhs(x, y) + rhs(x, y); } private: const LHS &lhs; const RHS &rhs; };

وهذه هي النسخة المُحدثة من ‎operator+()‎:

template < typename LHS, typename RHS> MatrixSum<LHS, RHS> operator+(const LHS &lhs, const LHS &rhs) { return MatrixSum<LHS, RHS> (lhs, rhs); }

كما ترى، لا يعيد ‎operator+()‎ "تقييمًا مُتسرّعًا" بعد الآن لنتيجَةِ إضافة نسختين من Matrix (والتي ستكون نسخة أخرى من Matrix)، ولكنه يعيد قالب تعبير يمثّل عملية الإضافة. ربما يجب أن تتذكّر أنّ التعبير لم يُقيَّم بعد، وإنما يخزّن مراجع إلى معاملاته وحسب. وفي الحقيقة، لا شيء يمنعك من إنشاء قالب تعبير ‎MatrixSum<>‎ كما يلي:

MatrixSum<Matrix < double>, Matrix<double>> SumAB(a, b);

يمكنك تقييم التعبير ‎d =‎‎a + ‎b‎ في مرحلة لاحقة، حين تحتاج فعليًا إلى نتيجة الجمع، كما يلي:

for (std::size_t y = 0; y != a.rows(); ++y) { for (std::size_t x = 0; x != a.cols(); ++x) { d(x, y) = SumAB(x, y); } }

كما ترى، هناك فائدة أخرى من استخدام قوالب التعبير وهي أنك ستتمكّن من تقييم مجموع ‎a‎ و ‎b‎ وإسناده إلى ‎d‎ مرّة واحد. أيضًا لا شيء يمنعك من الجمع بين عدّة قوالب تعبير، فقد يُنتج ‎a + b + c‎ قالب التعبير التالي:

MatrixSum<MatrixSum<Matrix<double>, Matrix<double> >, Matrix<double>> SumABC(SumAB, c);

وهنا، مرّة أخرى، يمكنك تقييم النتيجة النهائية مرّة واحدة:

for (std::size_t y = 0; y != a.rows(); ++y) { for (std::size_t x = 0; x != a.cols(); ++x) { d(x, y) = SumABC(x, y); } }

أخيرًا، آخر قطعة من اللغز هي توصيل قالب التعبير الخاص بك بالصنف ‎Matrix‎، من خلال تنفيذ العامل ‎Matrix::operator=()‎، الذي يأخذ قالب التعبير كوسيط ويقيِّمه في تمريرة واحدة كما فعلتَ "يدويًا" من قبل:

template < typename T, std::size_t COL, std::size_t ROW> class Matrix { public: using value_type = T; Matrix(): values(COL *ROW) {} static size_t cols() { return COL; } static size_t rows() { return ROW; } const T &operator()(size_t x, size_t y) const { return values[y *COL + x]; } T &operator()(size_t x, size_t y) { return values[y *COL + x]; } template < typename E> Matrix<T, COL, ROW> &operator=(const E &expression) { for (std::size_t y = 0; y != rows(); ++y) { for (std::size_t x = 0; x != cols(); ++x) { values[y *COL + x] = expression(x, y); } } return * this; } private: std::vector<T> values; }; هياكل بيانات قالب متغاير

الإصدار ≥ C++‎ 14

من المفيد أحيانًا تعريف أصناف أو بنيات متغايرة لا يُعرَّف عدد حقولها وأنواعها إلا في وقت التصريف، ويُعدُّ ‎std::tuple‎ أحد الأمثلة الأساسية على ذلك، لكن قد تحتاج أحيانًا إلى تعريف هياكلك المخصّصة. انظر المثال التالي الذي يعرّف بنية باستخدام التركيب (compounding) بدلاً من الوراثة كما هو الحال في ‎std::tuple‎.

سنبدأ بالتعريف العام (الفارغ)، والذي يمكن أن ينفع أيضًا كحالة أوّلية (base-case) لإنهاء التكرارية في التخصيص اللاحق:

template < typename...T > struct DataStructure {};

يسمح لنا هذا بتعريف بنية فارغة ‎DataStructure<> data‎، ولكنّ هذا غير مفيد حاليًا. بعد ذلك يأتي تخصيص الحالة التكرارية (recursive case specialisation):

template < typename T, typename...Rest > struct DataStructure<T, Rest... > { DataStructure(const T &first, const Rest &...rest): first(first), rest(rest...) {} T first; DataStructure < Rest... > rest; };

أصبح المثال مناسبًا الآن لإنشاء هياكل بيانات عشوائية، مثل <DataStructure<int, float, std::string‎، و("data(1, 2.1, "hello.

لاحظ أنّ هذا التخصيص يتطّلب وجود مُعامل قالب واحد على الأقل -وهو ‎T‎ أعلاه- دون إعارة اهتمام لخصوصيات الحُزمة ‎Rest‎، ويسمح إدراك وجود ‎T‎ بتعريف حقل ‎first‎. وتُحزَم بقية البيانات ذاتيًا على شكل ‎DataStructure‎ <‎Rest ...‎> ‎rest‎، ويهيّئ المنشئ كلا العُضوَين مع استدعاء مُنشئ ذاتي على العضو ‎rest‎.

لفهم هذا بشكل أفضل، إليك المثال التالي: لنفترض أنّ لديك تصريحًا <‎DataStructure<int,‎ ‎float‎. في البداية يتطابق التصريح مع التخصيص، وذلك يؤدّي إلى بنية تحتوي الحقلينint first و DataStructure<float> rest. ويتطابق التعريف ‎rest‎ مرّة أخرى مع التخصيص إذ يُنشئ حقلين float first و DataStructure<> rest خاصّين به. أخيرًا، يُطابَق rest مع الحالة الأساسية (base-case)، ممّا ينتج عنه بنية فارغة.

يمكنك تصور هذا على النحو التالي:

DataStructure<int, float> - > int first -> DataStructure<float> rest -> float first - > DataStructure < > rest -> (empty)

أصبح لدينا الآن بنية بيانات، ولكنّها ليست مفيدة حاليًا، إذ لا يمكننا الوصول بسهولة إلى العناصر الفردية للبيانات. على سبيل المثال، سيتعيّن علينا استخدام ‎data.rest.rest.first‎ للوصول إلى العضو الأخير في ‎DataStructure<int, float, std::string> data‎، وذلك صعب، لذا سنضيف تابع ‎get‎ إليها -مطلوب فقط في التخصيص، لأنّ بنية الحالة الأساسية لا تحتوي على بيانات أصلًا-:

template < typename T, typename...Rest > struct DataStructure<T, Rest... > { ... template < size_t idx> auto get() { return GetHelper<idx, DataStructure<T, Rest... >>::get(*this); } ... };

كما ترى، فإنّ التابع ‎get‎ نفسه مُقولَب -هذه المرّة على فهرس العضو المطلوب (لذلك يمكن استخدامه على النحو ‎data.get<1>()‎، على غرار الصفوف ‎std::tuple‎-، ويتم العمل الفعلي بفضل دالّة ساكنة في الصنف المساعد ‎GetHelper‎.

السبب في أنّنا لم نتمكن من تعريف الدالّة المطلوبة مباشرة في التابع ‎get‎ الخاص بـ ‎DataStructure‎ هو أنّنا (كما سنرى قريبًا) نحتاج إلى تخصيص ‎idx‎، وهذا مستحيل لأنّه لا يمكن تخصيص تابع القالب دون تخصيص قالب الصنف الحاوي. لاحظ أيضًا أنّ استخدام ‎auto‎ من نمط C++14 بسّط عملنا كثيرًا، إذ بدونها سيكون تعبير نوع القيمة المُعادة معقدًا.

سنحتاج إلى تصريح مُسبق فارغ وتَخصيصَين في الصنف المساعد.

أولا التصريح:

template < size_t idx, typename T> struct GetHelper;

في الحالة الأساسية (‎idx==‎ ‎0‎)، سنعيد العضو ‎first‎ فقط:

template < typename T, typename...Rest > struct GetHelper<0, DataStructure<T, Rest... >> { static T get(DataStructure<T, Rest... > &data) { return data.first; } };

في حالة التكرارية، سنُنقِص قيمة ‎idx‎ ونستدعي ‎GetHelper‎على العضو ‎rest‎:

template < size_t idx, typename T, typename...Rest > struct GetHelper<idx, DataStructure<T, Rest... >> { static auto get(DataStructure<T, Rest... > &data) { return GetHelper < idx - 1, DataStructure < Rest... >>::get(data.rest); } };

لنفترض أنّ لدينا ‎DataStructure<int, float> data‎ ونحتاج إلى ‎data.get<1>()‎، هذا سيَستدعي ‎GetHelper<1, DataStructure<int, float>>::get(data)‎ (التخصيص الثاني)، والذي سيستدعي بدوره ‎‎GetHelper<0, DataStructure<float>>::get(data.rest)‎، والتي تُعيد في النهاية (بحسب التخصيص الأوّل، إذ أنّ ‎idx‎ تساوي الآن 0) ‎data.rest.first‎.

هذا كل شيء! في ما يلي الشيفرة الكاملة، مع مثال توضيحي في الدالة ‎main‎:

#include <iostream> template < size_t idx, typename T> struct GetHelper; template < typename...T > struct DataStructure {}; template < typename T, typename...Rest > struct DataStructure<T, Rest... > { DataStructure(const T &first, const Rest &...rest): first(first), rest(rest...) {} T first; DataStructure < Rest... > rest; template < size_t idx> auto get() { return GetHelper<idx, DataStructure<T, Rest... >>::get(*this); } }; template < typename T, typename...Rest > struct GetHelper<0, DataStructure<T, Rest... >> { static T get(DataStructure<T, Rest... > &data) { return data.first; } }; template < size_t idx, typename T, typename...Rest > struct GetHelper<idx, DataStructure<T, Rest... >> { static auto get(DataStructure<T, Rest... > &data) { return GetHelper < idx - 1, DataStructure < Rest... >>::get(data.rest); } }; int main() { DataStructure<int, float, std::string > data(1, 2.1, "Hello"); std::cout << data.get<0> () << std::endl; std::cout << data.get<1> () << std::endl; std::cout << data.get<2> () << std::endl; return 0; } إعادة توجيه الوسائط

يمكن أن يقبل القالبُ المراجعَ اليمينية (rvalue references) واليسارية (lvalue references) على السواء باستخدام مرجع إعادة توجيه (forwarding reference):

template < typename T> void f(T && t);

في هذه الحالة، سيُستنبَط النوع الحقيقي لـ ‎t‎ من السياق:

struct X {}; X x; f(x); // f<X&>(x) تستدعي f(X()); // f < X>(x) تستدعي

في الحالة الأولى، يُستنتَج النوع ‎T‎ كمرجع إلى X ‏(‎X&‎)، أما نوع ‎t‎ فهو مرجع يساري إلى X، بينما في الحالة الثانية يُستنتَج نوع ‎T‎ كـ ‎X‎، ونوع ‎t‎ كمرجع يميني إلى X ‏(‎X&&‎).

ملاحظة: تجدر الإشارة إلى أنّه في الحالة الأولى، يكون ‎decltype(t)‎ و ‎T‎ متكافئان، وذلك على خلاف الحالة الثانية. ولأجل إعادة توجيه (forward)‏ ‎t‎ إلى دالّة أخرى بالشكل الصحيح، سواء كان مرجعًا يمينيا أو يساريا، فينبغي استخدام std::forward:

template < typename T> void f(T && t) { g(std::forward<T> (t)); }

يمكن استخدام "إعادة توجيه المراجع" (Forwarding references) مع القوالب المتغايرة (variadic templates):

template < typename...Args > void f(Args && ...args) { g(std::forward<Args> (args)...); }

ملاحظة: لا يمكن استخدام إعادة توجيه المراجع إلا مع معاملات القوالب، مثلًا في الشيفرة التالية، ‎v‎ هي مرجع يميني، وليست مرجع إعادة توجيه:

#include <vector> template < typename T> void f(std::vector<T> && v); التخصيص الجزئي للقوالب

على النقيض من التخصيص الكامل للقوالب، يسمح التخصيص الجزئي للقوالب بتقديم قالب مع بعض الوسائط الخاصّة بقالب آخر ثابت. ولا يُتاح التخصيص الجزئي للقوالب إلّا لأصناف وبنيات القالب:

// حالة شائعة template < typename T, typename U> struct S { T t_val; U u_val; }; // int حالة خاصة حيث معامل القالب الأول مثبّت عند النوع template < typename V> struct S<int, V> { double another_value; int foo(double arg) { // افعل شيئا ما } };

كما هو مُوضّح أعلاه، قد تُقدِّم التخصيصات الجزئية للقوالب مجموعات مختلفة تمامًا من البيانات والدوالّ العضوية. عند استنساخ قالب مخصّص جزئيًا، فسيتمّ اختيار التخصيص الأنسب، مثلًا لنعرّف قالبًا مع تخصيصَين جزئِيين:

template < typename T, typename U, typename V> struct S { static void foo() { std::cout << "General case\n"; } }; template < typename U, typename V> struct S<int, U, V> { static void foo() { std::cout << "T = int\n"; } }; template < typename V> struct S<int, double, V> { static void foo() { std::cout << "T = int, U = double\n"; } };

الاستدعاءات التالية:

S<std::string, int, double>::foo(); S<int, float, std::string>::foo(); S<int, double, std::string>::foo();

سوف تَطبع الخرج التالي:

General case T = int T = int, U = double

لا يمُكن أن تُخصّص قوالب الدوالّ جزئيًا:

template < typename T, typename U> void foo(T t, U u) { std::cout << "General case: " << t << " " << u << std::endl; } // حسنا template < > void foo<int, int> (int a1, int a2) { std::cout << "Two ints: " << a1 << " " << a2 << std::endl; } void invoke_foo() { foo(1, 2.1); // ==> "General case: 1 2.1" foo(1, 2); // =>> "Two ints: 1 2" } // Compilation error: partial function specialization is not allowed. template < typename U> void foo<std::string, U> (std::string t, U u) { std::cout << "General case: " << t << " " << u << std::endl; } تخصيص القوالب Template Specialization

يمكنك تنفيذ نُسخ مُحدّدة من صنف أو تابع قالب. على سبيل المثال، إذا كان لديك:

template < typename T> T sqrt(T t) { /*Some generic implementation */ }

فيمكنك إذن كتابة:

template < > int sqrt<int> (int i) { /* تنفيذ مُحسَّن للأعداد الصحيحة */ }

سيحصل المستخدم الذي يكتُب ‎sqrt(4.0)‎ على التنفيذ العام، بينما يحصل من يكتب ‎sqrt(4)‎ على التنفيذ المُخصّص.

كنى القوالب Alias templates

الإصدار ≥ C++‎ 11

انظر المثال البسيط التالي:

template < typename T > using pointer = T *;

هذا التعريف يجعل ‎pointer<T>‎ كُنيةً (alias) لـ ‎T*‎. مثلًا، يكافئ السطر التالي ;int* p = new int

pointer<int> p = new int;

لا يمكن تخصيص كُنى القوالب، لكن يمكن جعلها تشير إلى نوع مُتشعِّب في بنية:

template < typename T> struct nonconst_pointer_helper { typedef T * type; }; template < typename T> struct nonconst_pointer_helper < T const > { typedef T * type; }; template < typename T > using nonconst_pointer = nonconst_pointer_helper<T>::type; الاستنساخ الصريح

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

// print_string.h template < class T> void print_string(const T *str); // print_string.cpp #include "print_string.h" template void print_string(const char *); template void print_string(const wchar_t *);

ونظرًالأنّ ‎print_string<char>‎ و ‎print_string<wchar_t>‎ مُستنسختان بشكل صريح في ‎print_string.cpp‎، فسيتمكّن الرابط (linker) من العثور عليهما رغم أنّ القالب ‎print_string‎ لم يُعرَّف في الترويسة، وإذا لم تكن هذه التصريحات الفورية حاضرة، فمن المحتمل حدوث خطأ في الرابط.

تبيّن هذه الصفحة الأجنبية لماذا لا يمكن تقديم القوالب إلا في الترويسة.

الإصدار ≥ C++‎ 11

إذا سُبِق تعريف الاستنساخ الصريح بالكلمة ‎extern‎، فسيتحوّل إلى تصريح عن استنساخ صريح.

التصريح عن استنساخ صريح لتخصيص معيّن يمنع الاستنساخ الضمني لذلك التخصيص داخل وحدة الترجمة الحالية، لكن لا مانع أن يشير مرجع لذلك التخصيص -الذي كان من الممكن أن يتسبب في استنساخ ضمني- إلى تعريف استنساخ صريح في نفس وحدة الترجمة أو في غيرها.

foo.h‎

# ifndef FOO_H #define FOO_H template < class T > void foo(T x) { // تنفيذ معقّد }# endif

foo.cpp

#include "foo.h" // تعريف صريح لاستنساخ الحالات الشائعة. template void foo(int); template void foo(double);

main.cpp

#include "foo.h" // لها تعريف استنساخ صريح foo.cpp نعلم أنّ extern template void foo(double); int main() {

هنا تُستنسخ <foo<int، وذلك لا فائدة منه بما أن foo.cpp تقدم استنساخًا صريحًا سلفًا، نتابع:

foo(42);

وهنا، لن تُستنسخ <foo<double إذ تستخدم نسخة من <foo<double في foo.cpp بدلًا من ذلك، انظر بقية المثال:

foo(3.14); } مُعامل قالب غير نوعي

يُسمح لنا بالتصريح عن قيم التعبيرات الثابتة التي تفي بأحد المعايير التالية، وذلك خلا النوع كمعامِل قالب:

  • نوع عددي صحيح أو تعداد (enumeration).
  • مؤشّر إلى كائن أو مؤشّر إلى دالة.
  • مرجع يساري إلى كائن أو مرجع يميني إلى دالّة.
  • مؤشّر إلى عضو.
  • std::nullptr_t.

يمكن تحديد معاملات القالب غير النوعية بشكل صريح -مثل كل معاملات القوالب- أو اشتقاقها أو تحديد قيمها الافتراضية ضمنيًا عبر استنباط وسيط القالب. هذا مثال على استخدام مُعامل قالب غير نوعي، إذ سنمرر مصفوفة بالمرجع تتطلب حجمًا معينًا، ونحن نسمح بكل الأحجام باستخدام قالب size:

#include <iostream> template < typename T, std::size_t size> std::size_t size_of(T(&anArray)[size]) { return size; } int main() { char anArrayOfChar[15]; std::cout << "anArrayOfChar: " << size_of(anArrayOfChar) << "\n"; int anArrayOfData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; std::cout << "anArrayOfData: " << size_of(anArrayOfData) << "\n"; }

انظر المثال التالي على استخدام معاملات القالب النوعية وغير النوعية بشكل صريح، إذ يكون int معامِلًا نوعيًا، أما 5 فلا:

#include <array> int main() { std::array<int, 5> foo; }

تعدّ معاملات القوالب غير النوعية إحدى طرق تحقيق عوديّة القوالب والبرمجة العليا.

التصريح عن وسائط القوالب غير النوعية عبر auto

كان عليك قبل الإصدار C++‎ 17 أن تحدد نوع معامِل القالب غير النوعي عند كتابته، لذلك كان من الشائع كتابة شيء من هذا القبيل:

template < class T, T N> struct integral_constant { using type = T; static constexpr T value = N; }; using five = integral_constant<int, 5> ;

ولكن بالنسبة للتعبيرات المعقّدة، فإنّ استخدام الصيغة أعلاه يتطّلب كتابة ‎decltype(expr), expr‎ عند استنساخ القوالب، والحلّ هو تبسيط هذا المنظور واستخدام ‎auto‎:

الإصدار ≥ C++‎ 17

template < auto N> struct integral_constant { using type = decltype(N); static constexpr type value = N; }; using five = integral_constant<5> ; حاذف مخصّص للمؤشرات الحصرية unique_ptr

من أمثلة استخدام وسائط القوالب غير النوعية هي الجمع بين تحسين الأساس الفارغ وحاذف مُخصَّص للمؤشّرات الحصرية ‎unique_ptr‎. وتختلف حاذفات الواجهات البرمجية للغة C في نوع القيمة المعادة، ولكن هذا لا يشغلنا، فكل ما نريد هو شيء يعمل مع كل الدوالّ:

template < auto DeleteFn> struct FunctionDeleter { template < class T> void operator()(T *ptr) const { DeleteFn(ptr); } }; template<T, auto DeleteFn> using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter < DeleteFn>> ;

والآن يمكنك استخدام أيّ مؤشّر دالة يقبل وسيطًا من النوع ‎T‎ كمُعامل قالب غير نوعي، بصرف النظر عن نوع القيمة المُعادة، ودون القلق من أيّ حِمل إضافي في الحجم:

unique_ptr_deleter<std::FILE, std::fclose > p; معامِلات قالب القالب

قد نود أحيانًا أن نمرّر إلى القالب نوعَ قالبٍ دون تحديد قيمه، وهنا يأتي دور معاملات قالب القالب. هذا مثال بسيط يوضّح مفهوم مُعاملات قالب القالب:

template < class T> struct Tag1 {}; template < class T> struct Tag2 {}; template<template<class> class Tag> struct IntTag { typedef Tag<int> type; }; int main() { IntTag<Tag1>::type t; }

الإصدار ≥ C++‎ 11

#include <vector> #include <iostream> template < class T, template < class... > class C, class U > C<T> cast_all(const C<U> &c) { C<T> result(c.begin(), c.end()); return result; } int main() { std::vector<float> vf = { 1.2, 2.6, 3.7 }; auto vi = cast_all<int> (vf); for (auto && i: vi) { std::cout << i << std::endl; } } القيم الافتراضية لمُعاملات القالب

كما في حالة وسائط الدوال، يمكن أن يكون لمعاملات القالب قيمٌ افتراضية، ويجب التصريح عن معاملات القالب ذات القيمة الافتراضية في نهاية قائمة معاملات القالب، والهدف هو إتاحة حذف معاملات القالب ذات القيمة الافتراضية أثناء استنساخ القالب.

هذا مثال بسيط يوضّح كيفية استخدام القيمة الافتراضية لمُعامل القالب:

template < class T, size_t N = 10 > struct my_array { T arr[N]; }; int main() { /* N = 5 إهمال قيمة المعامل الافتراضية */ my_array<int, 5> a; /* 5 - a.arr اطبع طول */ std::cout << sizeof(a.arr) / sizeof(int) << std::endl; /* N = 10 المعامل الأخير محذوف */ my_array<int> b; /* 10 - a.arr اطبع طول*/ std::cout << sizeof(b.arr) / sizeof(int) << std::endl; } أنماط القوالب عجيبة التكرار CRTP

أنماط القوالب عجيبة التكرار (Curiously Recurring Template Pattern)، أو CRTP اختصارًا، هي أنماط برمجية يكون من الممكن فيها أن يرث صنف من قالب صنف، بحيث يكون ذلك الصنف نفسه أحد معامِلات القالب. وتُستخدم CRTP عادة لتوفير تعددية الأشكال الساكنة (static polymorphism) في C++‎.

وتُعدّ CRTP بديلًا ممتازًا وساكنًا (static) للدوالّ الوهمية والوراثة التقليدية، إذ يمكن استخدامها لتحديد خصائص الأنواع في وقت التصريف، ويقوم مبدأ عملها على جعل قالب صنف أساسي (base class template) يأخذ صنفًا مشتقًا منه كأحد معاملات القالب خاصته، وهذا يسمح بإجراء تحويل ساكن ‎static_cast‎ للمؤشّر ‎this‎ الخاص بالصنف الأساسي لكي يشير إلى الصنف المشتق.

بالطبع، هذا يعني أنّه سيتوجّب استخدام صنف CRTP دائمًا كصنف أساسي (base class) لصنف آخر، ويجب أن يمرِّر الصنف المشتق نفسه إلى الصنف الأساسي.

الإصدار ≥ C++‎ 14

لنفترض أنّ لديك مجموعة من الحاويات التي تدعم الدالّتين ‎begin()‎ و ‎end()‎، وتتطّلب المكتبة القياسية للحاويات المزيد من الدوالّ. يمكننا هنا أن نصمم صنف CRTP أساسي يوفّر مثل تلك الدولب استنادًا إلى ‎begin()‎ و ‎end()‎ فقط:

#include <iterator> template < typename Sub> class Container { private:

تعيد ()self مرجعًا إلى النوع المشتق، نتابع المثال:

Sub &self() { return * static_cast<Sub*> (this); } Sub const &self() const { return * static_cast< Sub const*> (this); } public: decltype(auto) front() { return* self().begin(); } decltype(auto) back() { return *std::prev(self().end()); } decltype(auto) size() const { return std::distance(self().begin(), self().end()); } decltype(auto) operator[](std::size_t i) { return *std::next(self().begin(), i); } };

يوفّر الصنف أعلاه الدوالّ ‎front()‎ و ‎back()‎ و ‎size()‎ و ‎operator[]‎ لأي صنف فرعي يوفر الدالتين ‎begin()‎ و ‎end()‎. في المثال التالي، يمكن للمصفوفات البسيطة المخصّصة ديناميكيًا أن تكون صنفًا فرعيًا:

#include <memory> // مصفوفة مخصّصة ديناميكيا template < typename T> class DynArray: public Container<DynArray < T>> { public: using Base = Container<DynArray < T>> ; DynArray(std::size_t size): size_ { size }, data_ { std::make_unique < T[] > (size_) } {} T* begin() { return data_.get(); } const T* begin() const { return data_.get(); } T* end() { return data_.get() + size_; } const T* end() const { return data_.get() + size_; } private: std::size_t size_; std::unique_ptr < T[] > data_; };

يمكن الآن لمستخدمي الصنف ‎DynArray‎ استخدام الواجهات التي يوفّرها الصنف الأساسي CRTP بسهولة على النحو التالي:

DynArray<int> arr(10); arr.front() = 2; arr[2] = 5; assert(arr.size() == 10);

فائدة النمط: يتجنّب هذا النمط استدعاءات الدوالّ الوهمية في وقت التشغيل، والتي تسعى لاجتياز التسلسل الهرمي للوراثة، ويعتمد بدلًا من ذلك على التحويلات الساكنة (static casts):

DynArray<int> arr(10); DynArray<int>::Base &base = arr; base.begin(); // لا استدعاءات وهمية

ويسمح التحويل الساكن الوحيد داخل الدالّة ‎begin()‎ في الصنف الأساسي ‎Container<DynArray<int>>‎ للمٌصرّف بتحسين الشيفرة بشكل كبير، إذ لن يحدث أي تنقيب في الجدول الوهمي (virtual table) في وقت التشغيل.

عيوب النمط: نظرًا لأنّ الصنف الأساسي مُقوْلَب ويختلف من مصفوفة ‎DynArray‎ إلى أخرى، فلا يمكن تخزين المؤشّرات التي تشير إلى أصنافها الأساسية في مصفوفة متجانسة كما نفعل عمومًا مع الوراثة العادية التي لا يعتمد فيها الصنف الأساسي على الصنف المشتق:

class A {}; class B: public A {}; A *a = new B; استخدام أنماط CRTP لتجنّب تكرار الشيفرة

يوضّح المثال التالي كيفية استخدام CRTP لتجنّب تكرار الشيفرة:

struct IShape { virtual~IShape() = default; virtual void accept(IShapeVisitor &) const = 0; }; struct Circle: IShape {

يجب على كل شكل أن يقدم هذا التابع بنفس الطريقة … ، نتابع المثال:

void accept(IShapeVisitor & visitor) const override { visitor.visit(*this); } // ... }; struct Square: IShape {

بالمثل هنا، يجب على كل شكل أن يقدم هذا التابع بنفس الطريقة، نتابع:

void accept(IShapeVisitor & visitor) const override { visitor.visit(*this); } // ... };

يحتاج كل نوع فرعي من ‎IShape‎ إلى تنفيذ نفس الدالّة بنفس الطريقة، وهذا قد يأخذ الكثير من الوقت. الحل البديل هو تنفيذ نوع جديد في الهرمية الوراثية يتكفّل بفعل ذلك نيابة عنّا:

template < class Derived> struct IShapeAcceptor: IShape { void accept(IShapeVisitor & visitor) const override { visitor.visit(*static_cast< Derived const*> (this)); } };

والآن، يكفي أن ترث الأشكال من المتقبِّل (acceptor):

struct Circle: IShapeAcceptor < Circle> { Circle(const Point &center, double radius): center(center), radius(radius) {} Point center; double radius; }; struct Square: IShapeAcceptor < Square> { Square(const Point &topLeft, double sideLength): topLeft(topLeft), sideLength(sideLength) {} Point topLeft; double sideLength; };

لم تعد هناك حاجة الآن إلى تكرار الشيفرة.

هذا الدرس جزء من سلسلة دروس عن C++‎.

ترجمة -بتصرّف- للفصل Chapter 78: Expression templates والفصل Chapter 79: Curiously Recurring Template Pattern (CRTP)‎ من كتاب C++ Notes for Professionals

28,915 سامسونج تطلق جالكسي Tab S7 في 18 سبتمبر الجاري بسعر يبدأ من 650 دولار

أعلنت شركة سامسونج عن إطلاقها أجهزتها اللوحية الجديدة جالكسي Tab S7 و Tab S7+ يوم 18 سبتمبر الجاري بينما فتحت باب الطلب المسبق بداية من اليوم.

يبدأ سعر اللوحي جالكسي الجديد من سامسونج يـ 650 دولار أمريكي بشاشة LCD قياس 11 بوصة ذات معدل تحديث 120 هيرتز تعد جيدة لكنها ليست الأفصل، أما بالنسبة للنسخة الراقية بلس فيبدأ سعرها من 850 دولار أمريكي وتأتي بشاشة 12.4 بوصة من نوع OLED مع معدل تحديث 120 هيرتز.

كلتا النسحتين تدعمان اتصال WiFi ومزودتان بقلم إلكتروني إلا أن لوحة المفاتيح تأتي منفردة لكن إذا رغبت في اقتنائها فالشركة توفر خصم بنسة 50% في حال ابتعتها مع النسخة المناسبة لاحتياجاتك.

حيث ستحصل على لوحة المفاتيح التي تناسب اللوحي العادي Tab S7 بسعر 100 دولار بدلاً من السعر الطبيعي 200 دولار بينما الخاصة لوحة نسخة بلس فسيكون سعرها مع الخصم 115 دولار بدلاً من 230 دولار.

فيما ستتوفر نسخ خاصة من اللوحي تدعم اتصال الحيل الخامس 5G في الولايات المتحدة الأمريكية من شركة Verizon بحيث سيبلغ سعر جالكسي Tab S7 5G قرابة 850 دولار أمريكي بينما Tab S7 بلس 1,050 دولار أمريكي مع كون العرض على لوحة المفاتيح سكيون مماثل بخصم 50% كما هو الحال مع نسخة 4G LTE.

يُشار أن سامسونج أعلنت اليوم عن هاتف جالكسي A42 5G والذي يُعد أرخص هاتف يدعم الجيل الخامس في العالم.

كما كشفت أمس عن هاتف Galaxy Z Fold2 القابل للطي مع تصميم جديد وتحسينات في الأداء والشاشة والهيكلية.

المصدر:

The Verge

التدوينة سامسونج تطلق جالكسي Tab S7 في 18 سبتمبر الجاري بسعر يبدأ من 650 دولار ظهرت أولاً على عالم التقنية.

28,909 الأطفال ينقلون فيروس كورونا حتى بدون ظهور أعراض عليهم دراسات أمريكية جديدة توصلت إلى أن أثر الفيروسات لدى الأطفال والشبان كبير، وحتى بدون ظهور أعراض عليهم يمكن أن ينلقوا العدوى لغيرهم طوال أسابيع. فماذا يعني هذا بالنسبة إلى دور الحضانة والمدارس؟

الصفحات

أنت هنا