C++11
C++11 هي نسخة من معيار تقني مشترك ، ISO/IEC 14882، صادر عن المنظمة الدولية للتوحيد القياسي (ISO) واللجنة الكهروتقنية الدولية (IEC)، للغة البرمجة C++ . حلت C++11 محل النسخة السابقة من معيار C++، المسماة C++03 ، [ 1 ] ثم حلت محلها لاحقًا C++14 . يتبع الاسم تقليد تسمية إصدارات اللغة بسنة نشر المواصفات، مع العلم أنها كانت تُسمى سابقًا C++0x لأنه كان من المتوقع نشرها قبل عام 2010. [ 2 ]
على الرغم من أن أحد أهداف التصميم كان تفضيل التغييرات على المكتبات على التغييرات على اللغة الأساسية ، [ 3 ] إلا أن C++11 أضافت العديد من التحسينات إلى اللغة الأساسية. تشمل جوانب اللغة الأساسية التي شهدت تحسينات كبيرة دعم تعدد الخيوط، ودعم البرمجة العامة ، والتهيئة الموحدة، والأداء. كما أُجريت تغييرات جوهرية على مكتبة C++ القياسية ، حيث تم دمج معظم مكتبات التقرير الفني الأول (TR1) الخاص بـ C++ ، باستثناء مكتبة الدوال الرياضية الخاصة. [ 4 ]
نُشرت لغة C++11 كمعيار ISO/IEC 14882:2011 [ 5 ] في سبتمبر 2011، وهي متاحة مقابل رسوم. أما مسودة العمل الأقرب إلى معيار C++11 المنشور فهي N3337، بتاريخ 16 يناير 2012؛ [ 6 ] وهي لا تحتوي إلا على تصحيحات تحريرية من معيار C++11. [ 7 ]
كانت لغة C++11 مدعومة بالكامل بواسطة Clang 3.3 والإصدارات اللاحقة، [ 8 ] وبواسطة مجموعة مترجمات GNU (GCC) 4.8.1 والإصدارات اللاحقة. [ 9 ]
أهداف التصميم
سعت لجنة التصميم إلى الالتزام بعدد من الأهداف عند تصميم لغة C++11:
- الحفاظ على الاستقرار والتوافق مع الإصدارات القديمة من التعليمات البرمجية
- يفضل إدخال ميزات جديدة عبر المكتبة القياسية، بدلاً من توسيع اللغة الأساسية
- تحسين لغة C++ لتسهيل تصميم الأنظمة والمكتبات، بدلاً من إدخال ميزات جديدة مفيدة فقط لتطبيقات محددة.
- زيادة سلامة النوع من خلال توفير بدائل أكثر أمانًا للتقنيات غير الآمنة السابقة
- زيادة الأداء والقدرة على العمل مباشرة مع الأجهزة
- تقديم حلول مناسبة لمشاكل العالم الحقيقي
- اجعل لغة C++ سهلة التعليم والتعلم دون إزالة أي أداة يحتاجها المبرمجون الخبراء
يُعتبر الاهتمام بالمبتدئين أمراً بالغ الأهمية، لأن معظم مبرمجي الحاسوب سيظلون كذلك، ولأن العديد من المبتدئين لا يوسعون معارفهم، ويقتصرون على العمل في جوانب اللغة التي يتخصصون فيها. [ 2 ] [ 3 ]
إضافات للغة C++ الأساسية
تتمثل إحدى وظائف لجنة لغة C++ في تطوير نواة اللغة. وتشمل جوانب نواة اللغة التي شهدت تحسينات كبيرة دعم تعدد الخيوط ، ودعم البرمجة العامة ، والتهيئة الموحدة، والأداء.
تحسينات في أداء وقت تشغيل اللغة الأساسية
توجد ميزات اللغة هذه في المقام الأول لتوفير نوع من فوائد أداء وقت التشغيل ، سواء من حيث الذاكرة أو سرعة الحوسبة.
مراجع القيمة اليمنى ومنشئات النقل
في لغة C++03 (وما قبلها)، كان من المفترض ألا تكون القيم المؤقتة (التي تُسمى " قيم يمين "، لأنها غالبًا ما تقع على الجانب الأيمن من عملية الإسناد) قابلة للتعديل أبدًا - تمامًا كما هو الحال في لغة C - وكانت تُعتبر غير قابلة للتمييز عن الأنواع؛ ومع ذلك، في بعض الحالات، كان من الممكن تعديل القيم المؤقتة، وهو سلوك كان يُعتبر ثغرة مفيدة. [ 10 ] تُضيف لغة C++11 نوع مرجعي جديد غير ثابت يُسمىconst T&مرجع القيمة اليمنى ، الذي يتم تحديده بواسطة. يشير هذا إلى المتغيرات المؤقتة التي يُسمح بتعديلها بعد تهيئتها، وذلك لغرض السماح بـ "دلالات النقل".T&&
تُعدّ النسخ العميقة المكلفة وغير الضرورية التي قد تحدث ضمنيًا عند تمرير الكائنات بالقيمة مشكلة أداء مزمنة في لغة C++03. لتوضيح هذه المشكلة، لنفترض أن `in` std::vector<T>هو، داخليًا، غلافٌ حول مصفوفة على نمط لغة C ذات حجم مُحدد. إذا std::vector<T>تم إنشاء كائن مؤقت أو إرجاعه من دالة، فلا يُمكن تخزينه إلا بإنشاء كائن جديد std::vector<T>ونسخ جميع بيانات القيمة المُعادة إليه. عندئذٍ، يتم حذف الكائن المؤقت وكل ما يحتويه من ذاكرة. (لتبسيط الشرح، نتجاهل هنا تحسين القيمة المُعادة ).
في لغة C++11، أيمكن لدالة البناء التيstd::vector<T>تأخذ مرجع قيمة يمين إلى كائن مؤقتstd::vector<T>نسخ المؤشر إلى مصفوفة C الداخلية من قيمة اليمين إلى الكائن الجديدstd::vector<T>، ثم تعيين قيمة المؤشر داخل قيمة اليمين إلى null. بما أن الكائن المؤقت لن يُستخدم مرة أخرى، فلن يحاول أي كود الوصول إلى المؤشر الفارغ، ولأن المؤشر فارغ، فلن تُحذف ذاكرته عند خروجه من نطاق التعريف. وبالتالي، لا تتجنب هذه العملية تكلفة النسخ العميق فحسب، بل إنها آمنة وغير مرئية.
Rvalue references can provide performance benefits to existing code without needing to make any changes outside the standard library. The type of the returned value of a function returning an std::vector<T> temporary does not need to be changed explicitly to std::vector<T>&& to invoke the move constructor, as temporaries are considered rvalues automatically. (However, if std::vector<T> is a C++03 version without a move constructor, then the copy constructor will be invoked with a const std::vector<T>, incurring a significant memory allocation.)
For safety reasons, some restrictions are imposed. A named variable will never be considered to be an rvalue even if it is declared as such. To get an rvalue, the function template std::move() should be used. Rvalue references can also be modified only under certain circumstances, being intended to be used primarily with move constructors.
Due to the nature of the wording of rvalue references, and to some modification to the wording for lvalue references (regular references), rvalue references allow developers to provide perfect function forwarding. When combined with variadic templates, this ability allows for function templates that can perfectly forward arguments to another function that takes those particular arguments. This is most useful for forwarding constructor parameters, to create factory functions that will automatically call the correct constructor for those particular arguments. This is seen in the emplace_back set of the C++ standard library methods.
constexpr – Generalized constant expressions
C++ has always had the concept of constant expressions. These are expressions such as 3 + 4 that will always yield the same results, at compile time and at runtime. Constant expressions are optimization opportunities for compilers, and compilers frequently execute them at compile time and hardcode the results in the program. Also, in several places, the C++ specification requires using constant expressions. Defining an array requires a constant expression, and enumerator values must be constant expressions.
However, a constant expression has never been allowed to contain a function call or object constructor. So a piece of code as simple as this is invalid:
intgetFive(){return5;}intsomeValue[getFive()+7];// Create an array of 12 integers. Ill-formed C++This was not valid in C++03, because getFive() + 7 is not a constant expression. A C++03 compiler has no way of knowing if getFive() actually is constant at runtime. In theory, this function could affect a global variable, call other non-runtime constant functions, etc.
قدّمت لغة C++11 الكلمة المفتاحية ` constexprconstructor`، التي تُمكّن المستخدم من ضمان أن يكون مُنشئ الدالة أو الكائن ثابتًا وقت الترجمة. [ 11 ] يمكن إعادة كتابة المثال أعلاه على النحو التالي:
constexpr int getFive () { return 5 ; }int someValue [ getFive () + 7 ]; // إنشاء مصفوفة من 12 عددًا صحيحًا. متوافق مع C++11وهذا يسمح للمترجم بفهم والتحقق من أن هذا getFive()ثابت وقت الترجمة.
استخدام constexpr`on` مع دالة يفرض بعض القيود على وظائفها. أولًا، يجب أن يكون نوع إرجاع الدالة غير فارغ. ثانيًا، لا يمكن لجسم الدالة تعريف متغيرات أو أنواع جديدة. ثالثًا، يجب أن يقتصر جسم الدالة على التصريحات، وعبارات `null`، وعبارة إرجاع واحدة. يجب أن توجد قيم للوسائط بحيث ينتج عن استبدال الوسائط في عبارة الإرجاع تعبير ثابت.
قبل C++11، كان بالإمكان استخدام قيم المتغيرات في التعبيرات الثابتة فقط إذا تم تعريف المتغيرات على أنها ثابتة (const)، وكان لها مُهيئ عبارة عن تعبير ثابت، وكانت من نوع عدد صحيح أو تعداد. أما C++11 فقد أزالت هذا القيد الذي يشترط أن تكون المتغيرات من نوع عدد صحيح أو تعداد إذا تم تعريفها باستخدام الكلمة constexprالمفتاحية التالية:
constexpr double EARTH_GRAVITATIONAL_ACCELERATION = 9.8 ; constexpr double MOON_GRAVITATIONAL_ACCELERATION = EARTH_GRAVITATIONAL_ACCELERATION / 6.0 ;تكون متغيرات البيانات هذه ثابتة ضمنيًا، ويجب أن يكون لها مُهيئ يجب أن يكون تعبيرًا ثابتًا.
لإنشاء قيم بيانات تعبيرية ثابتة من أنواع معرفة من قبل المستخدم، يمكن أيضًا تعريف الدوال البانية باستخدام ` constexpr. constexprيمكن أن يحتوي جسم الدالة البانية على تعريفات وعبارات فارغة فقط، ولا يمكنه تعريف متغيرات أو تحديد أنواع، كما هو الحال مع constexprالدالة. يجب أن توجد قيم وسيطة بحيث، بعد استبدال الوسيطة، يتم تهيئة أعضاء الفئة بتعبيرات ثابتة. يجب أن تكون الدوال الهدامة لهذه الأنواع بسيطة.
constexprيُفضّل عادةً تعريف دالة النسخ البانية لنوعٍ ما، والتي تحتوي على أي دوال بانية constexpr، كدالة بانية أيضًا، وذلك للسماح بإرجاع كائنات من هذا النوع كقيمة من دالة constexpr. يمكن تعريف أي دالة عضو في فئة، مثل دوال النسخ البانية، وتحميل المعاملات الزائد، وما إلى ذلك، كدالة constexpr، constexprطالما أنها تستوفي متطلبات دوال constexpr. يسمح هذا للمترجم بنسخ الكائنات في وقت الترجمة، وإجراء عمليات عليها، وما إلى ذلك.
إذا تم استدعاء دالة أو مُنشئ من نوع constexpr باستخدام وسائط ليست تعبيرات ثابتة، فسيتصرف الاستدعاء كما لو أن الدالة ليست من نوع constexpr، ولن تكون القيمة الناتجة تعبيرًا ثابتًا. وبالمثل، إذا لم يُقيّم التعبير الموجود في عبارة الإرجاع لدالة من نوع constexpr إلى تعبير ثابت عند استدعاء معين، فلن تكون النتيجة تعبيرًا ثابتًا.
constexprيختلف عن consteval، الذي تم تقديمه في C++20 ، في أن الأخير يجب أن ينتج دائمًا ثابتًا في وقت الترجمة ، بينما constexprلا يوجد هذا القيد في .
تعديل تعريف البيانات القديمة البسيطة
في لغة C++03، يجب أن يلتزم الصنف أو البنية بعدد من القواعد ليُعتبر نوع بيانات بسيط (POD). تُنتج الأنواع التي تُطابق هذا التعريف تخطيطات كائنات متوافقة مع لغة C، ويمكن تهيئتها بشكل ثابت. يفرض معيار C++03 قيودًا على الأنواع المتوافقة مع لغة C أو التي يمكن تهيئتها بشكل ثابت، حتى في حال عدم وجود سبب تقني يمنع المُصرّف من قبول البرنامج. فإذا أنشأ شخص ما نوع POD في C++03 وأضاف إليه دالة عضو افتراضية، فلن يكون هذا النوع نوع POD، ولن يكون بالإمكان تهيئته بشكل ثابت، وسيكون غير متوافق مع لغة C، حتى في حال عدم تغيير تخطيط الذاكرة.
خففت لغة C++11 العديد من قواعد POD، من خلال تقسيم مفهوم POD إلى مفهومين منفصلين: التافه والتخطيط القياسي .
يمكن تهيئة النوع البسيط بشكل ثابت. وهذا يعني أيضًا أنه من الصحيح نسخ البيانات باستخدام `const` std::memcpy()، بدلاً من استخدام مُنشئ النسخ. يبدأ عمر النوع البسيط عند تعريف مساحة التخزين الخاصة به، وليس عند اكتمال المُنشئ.
يُعرَّف الصنف أو البنية التافهة بأنها تلك التي:
- يحتوي على مُنشئ افتراضي بسيط. قد يستخدم هذا صيغة المُنشئ الافتراضي (
X() = default;). - يحتوي على دوال إنشاء نسخ ونقل بسيطة، والتي قد تستخدم الصيغة الافتراضية.
- يحتوي على عوامل نسخ ونقل بسيطة، والتي قد تستخدم الصيغة الافتراضية.
- يحتوي على مُدمِّر بسيط، والذي يجب ألا يكون افتراضيًا.
تكون الدوال البانية بسيطة فقط في حال عدم وجود دوال أعضاء افتراضية في الصنف وعدم وجود أصناف أساسية افتراضية. كما تتطلب عمليات النسخ/النقل أن تكون جميع عناصر البيانات غير الثابتة بسيطة.
يعني النوع ذو التخطيط القياسي أنه يرتب ويحزم أعضاءه بطريقة متوافقة مع لغة C. ويُعتبر الصنف أو البنية ذو تخطيط قياسي، بحكم التعريف، بشرط:
- لا يحتوي على وظائف افتراضية
- لا يحتوي على فئات أساسية افتراضية
- جميع عناصر البيانات غير الثابتة لها نفس التحكم في الوصول (عام، خاص، محمي)
- جميع عناصر البيانات غير الثابتة، بما في ذلك أي عنصر في فئاتها الأساسية، موجودة في نفس الفئة في التسلسل الهرمي.
- تنطبق القواعد المذكورة أعلاه أيضًا على جميع الفئات الأساسية وعلى جميع عناصر البيانات غير الثابتة في التسلسل الهرمي للفئات.
- لا يحتوي على فئات أساسية من نفس نوع أول عضو بيانات غير ثابت مُعرَّف
يعتبر الصنف/البنية/الاتحاد كائنًا بسيطًا إذا كان بسيطًا، وذو تخطيط قياسي، وجميع أعضاء البيانات غير الثابتة والفئات الأساسية الخاصة به هي كائنات بسيطة.
بفصل هذين المفهومين، يصبح من الممكن الاستغناء عن أحدهما دون فقدان الآخر. قد لا يكون الصنف ذو دوال البناء المعقدة (نقل ونسخ) بسيطًا، ولكنه قد يكون متوافقًا مع التنسيق القياسي، وبالتالي يمكن استخدامه مع لغة C. وبالمثل، فإن الصنف ذو أعضاء البيانات غير الثابتة العامة والخاصة لن يكون متوافقًا مع التنسيق القياسي، ولكنه قد يكون بسيطًا، وبالتالي memcpyيمكن استخدامه مع لغة C.
تحسينات في أداء وقت بناء اللغة الأساسية
قالب خارجي
في لغة C++03، يجب على المُصرّف إنشاء نسخة من القالب كلما صادف قالبًا مُحددًا بالكامل في وحدة ترجمة. إذا تم إنشاء نسخة من القالب بنفس الأنواع في العديد من وحدات الترجمة، فقد يؤدي ذلك إلى زيادة كبيرة في أوقات الترجمة. لا توجد طريقة لمنع ذلك في C++03، لذا قدمت C++11 تعريفات القوالب الخارجية، على غرار تعريفات البيانات الخارجية.
يحتوي C++03 على هذا التركيب النحوي لإلزام المترجم بإنشاء قالب:
قالب فئة std :: vector <X> ;يوفر الإصدار C++11 الآن هذه الصيغة:
extern template class std :: vector < X > ;وهذا يخبر المترجم بعدم إنشاء القالب في وحدة الترجمة هذه.
تحسينات في سهولة استخدام اللغة الأساسية
توجد هذه الميزات لغرض أساسي هو تسهيل استخدام اللغة. فهي تُحسّن سلامة الأنواع، وتقلل من تكرار التعليمات البرمجية، وتقلل من احتمالية وجود أخطاء في التعليمات البرمجية، وما إلى ذلك.
قوائم التهيئة
استعارت لغة C++03 ميزة قوائم التهيئة من لغة C. تُعطى البنية أو المصفوفة قائمة من الوسائط بين قوسين معقوفين، بترتيب تعريفات العناصر في البنية. قوائم التهيئة هذه قابلة للتكرار، لذا يمكن لمصفوفة من البنى أو بنية تحتوي على بنى أخرى استخدامها.
struct FloatAndInt { float first ; int second ; };FloatAndInt scalar = { 0.43f , 10 }; // كائن واحد، حيث first=0.43f و second=10 FloatAndInt a [] = {{ 13.4f , 3 }, { 43.28f , 29 }, { 5.934f , 17 }}; // مصفوفة من ثلاثة كائناتيُعدّ هذا مفيدًا جدًا للقوائم الثابتة، أو لتهيئة بنية بيانات بقيمة معينة. توفر لغة C++ أيضًا دوالًا بانية لتهيئة كائن، لكنها غالبًا ما تكون أقل ملاءمة من قائمة التهيئة. مع ذلك، يسمح معيار C++03 باستخدام قوائم التهيئة فقط مع البنى والفئات التي تتوافق مع تعريف البيانات البسيطة القديمة (POD)؛ بينما يُوسّع معيار C++11 قوائم التهيئة، بحيث يمكن استخدامها مع جميع الفئات، بما في ذلك الحاويات القياسية مثل std::vector<T>.
يربط C++11 المفهوم بقالب يُسمى `<template>` std::initializer_list<T>. يسمح هذا للمنشئات والوظائف الأخرى بأخذ قوائم التهيئة كمعاملات. على سبيل المثال:
باستخدام std :: initializer_list ؛class MySequence { public : MySequence ( initializer_list < int > list ); };وهذا يسمح MySequenceبإنشائها من سلسلة من الأعداد الصحيحة، مثل:
MySequence s = { 1 , 4 , 5 , 6 };هذا المُنشئ نوع خاص من المُنشئات، يُسمى مُنشئ قائمة التهيئة. تُعامل الفئات التي تحتوي على هذا المُنشئ معاملة خاصة أثناء التهيئة الموحدة (انظر أدناه ).
std::initializer_list<T>يُعدّ الصنف القالب نوعًا أساسيًا في مكتبة C++11 القياسية. ويمكن لمترجم C++11 إنشاء هذه الأصناف بشكل ثابت باستخدام {}الصيغة دون تحديد اسم النوع في السياقات التي تُشير فيها هذه الأقواس إلى نوع std::initializer_list<T>، أو بتحديد النوع صراحةً std::initializer_list<T>{args}(وهكذا بالنسبة لأنواع أخرى من صيغ الإنشاء).
يمكن نسخ القائمة بعد إنشائها، وهي عملية سريعة وغير مكلفة، وتعمل كنسخة بالمرجع (عادةً ما يتم تنفيذ الفئة كزوج من مؤشرات البداية/النهاية). القائمة std::initializer_list<T>ثابتة: لا يمكن تغيير عناصرها بعد إنشائها، ولا يمكن تغيير البيانات الموجودة في تلك العناصر (مما يمنع نقل البيانات منها، ويتطلب نسخها إلى عناصر الفئة، وما إلى ذلك).
على الرغم من أن عملية بناء هذا النوع تُعامل بشكل خاص من قِبل المُصرّف، إلا std::initializer_list<T>أنه نوع حقيقي، وبالتالي يمكن استخدامه في أماكن أخرى غير مُنشئات الفئات. يمكن للدوال العادية أن تأخذ std::initializer_list<T>أنواعًا مُحددة كوسائط. على سبيل المثال:
باستخدام std :: initializer_list ؛void foo ( initializer_list < float > list ); // النسخ غير مكلف؛ انظر أعلاهfoo ({ 1.0f , -3.45f , -0.4f });تتضمن أمثلة ذلك في المكتبة القياسية std::min()القوالب std::max()التي تأخذ std::initializer_list<T>قيمًا من النوع الرقمي.
يمكن أيضًا تهيئة الحاويات القياسية بهذه الطرق:
باستخدام std :: string ؛ باستخدام std :: vector ؛vector <string> v = { " xyzzy " , "plugh" , "abracadabra" }; vector<string> v ( { " xyzzy " , " plugh " , " abracadabra" }); vector <string> v { "xyzzy" , "plugh" , "abracadabra" }; // انظر "التهيئة الموحدة" أدناهتهيئة موحدة
يُعاني C++03 من عدة مشاكل في تهيئة الأنواع. توجد عدة طرق للقيام بذلك، وبعضها يُنتج نتائج مختلفة عند تبديلها. على سبيل المثال، قد تبدو صيغة المُنشئ التقليدية وكأنها تعريف دالة، ويجب اتخاذ خطوات لضمان عدم اعتبارها كذلك من قِبل قاعدة التحليل الأكثر تعقيدًا في المُصرّف . يُمكن تهيئة أنواع التجميع وأنواع البيانات البسيطة فقط باستخدام مُهيئات التجميع (باستخدام ).Tx={/* initialization */};
توفر لغة C++11 صيغة تسمح بتهيئة أنواع البيانات بشكل موحد تمامًا، وتعمل على أي كائن. وهي توسع صيغة قائمة التهيئة.
struct IntAndFloat1 { int x ; double y ; };struct IntAndFloat2 { private : int x ; double y ; public : IntAndFloat2 ( int x , double y ) : x { x }, y { y } {} };IntAndFloat1 v1 { 5 , 3.2 }; IntAndFloat2 v2 { 2 , 4.3 };تُعامل عملية التهيئة v1تمامًا كما لو كانت تهيئة تجميعية. أي أن كل عنصر بيانات في الكائن، بدوره، سيُهيأ بنسخ القيمة المقابلة له من قائمة التهيئة. سيتم استخدام تحويل النوع الضمني عند الحاجة. إذا لم يكن هناك تحويل، أو كان هناك تحويل تضييق فقط، فسيكون البرنامج غير صحيح. تستدعي عملية التهيئة v2الدالة البانية.
ويمكن للمرء أن يفعل هذا أيضاً:
باستخدام std :: string ;struct IdString { string name ; int identifier ; };IdString getString () { return { "foo" , 42 }; // لاحظ عدم وجود نوع صريح. }لا يُغني التهيئة الموحدة عن صيغة المُنشئ، التي لا تزال مطلوبة في بعض الأحيان. إذا كان للصنف مُنشئ قائمة تهيئة ، فإنه يُعطى الأولوية على أشكال الإنشاء الأخرى، شريطة أن تتوافق قائمة التهيئة مع نوع مُنشئ التسلسل. يحتوي إصدار C++11 من الصنف على مُنشئ قائمة تهيئة لنوع القالب الخاص به. وبالتالي، يكون هذا الكود كالتالي:T(initializer_list<X>v);std::vector<T>
std :: vector <int> vec {4} ;سيتم استدعاء مُنشئ قائمة التهيئة، وليس مُنشئ الكائن std::vectorالذي يأخذ مُعامل حجم واحد ويُنشئ المتجه بهذا الحجم. للوصول إلى مُنشئ الكائن الأخير، سيحتاج المستخدم إلى استخدام صيغة المُنشئ القياسية مباشرةً.
استنتاج النوع
في لغة C++03 (وكذلك لغة C)، يتطلب استخدام متغير تحديد نوعه صراحةً. مع ذلك، ومع ظهور أنواع القوالب وتقنيات البرمجة الوصفية للقوالب، قد يصعب تحديد نوع شيء ما، لا سيما القيمة المُعادة من دالة. لذا، يُعد تخزين القيم الوسيطة في متغيرات أمرًا صعبًا، وقد يتطلب معرفة تفاصيل مكتبة البرمجة الوصفية المستخدمة.
يُتيح C++11 التخفيف من هذه المشكلة بطريقتين. أولاً، يمكن استخدام الكلمة autoالمفتاحية `<input type="value">` لتعريف متغير ذي قيمة ابتدائية صريحة. [ 12 ] [ 13 ] وهذا يُنشئ متغيرًا من النوع المحدد للقيمة الابتدائية.
باستخدام مساحة الاسم std :: placeholders ؛auto callable = std :: bind ( & foo , _2 , _1 , x ); auto y = 5 ;نوع callableهو ببساطة ما تُرجعه دالة القالب المُعدّلة std::bind()لتلك الوسائط. يُمكن للمُصرّف تحديد هذا النوع بسهولة إجرائيًا كجزء من مهام التحليل الدلالي، لكن يصعب على المستخدم تحديده بمجرد الفحص. نوع مُحدّد yجيدًا أيضًا، لكن يسهل على المستخدم تحديده. إنه int، وهو نفس نوع القيمة العددية الصحيحة.
إن استخدام هذه الكلمة المفتاحية autoفي لغة C++ يعيد توظيف دلالات هذه الكلمة المفتاحية، والتي تم استخدامها في الأصل في اللغة السابقة غير المصنفة B في دور ذي صلة يتمثل في الإشارة إلى تعريف متغير تلقائي غير مصنف .
علاوة على ذلك، يمكن استخدام الكلمة المفتاحية decltypeلتحديد نوع التعبير في وقت الترجمة. على سبيل المثال:
int i = 0 ; decltype ( i ) j = 5 ;يُعدّ هذا أكثر فائدةً عند استخدامه مع auto، لأن نوع المتغير التلقائي لا يعرفه إلا المُصرّف. مع ذلك، decltypeيُمكن أن يكون مفيدًا جدًا أيضًا للتعبيرات في الشيفرة التي تستخدم بكثرة تحميل المعاملات والأنواع المُخصصة.
autoكما أنه مفيد لتقليل الإسهاب في كتابة الكود. على سبيل المثال، بدلاً من كتابة
باستخدام std :: vector ;for ( vector < int >:: const_iterator it = v . cbegin (); it != v . cend (); ++ it ) { /* حلقة تكرار */ }يمكن للمبرمج استخدام الصيغة الأقصر
for ( auto it = v.cbegin ( ); it ! = v.cend ( ); ++ it ) { /* حلقة تكرارية * / }والتي يمكن ضغطها بشكل أكبر نظرًا لأنها vتُنفذ begin()/ end()تُكرر:
for ( const auto & x : v ) { /* حلقة تكرارية */ }يتزايد هذا الاختلاف عندما يبدأ المبرمج في تداخل الحاويات، على الرغم من أنه في مثل هذه الحالات typedefيمكن استخدام s لتقليل كمية التعليمات البرمجية.
يمكن أن يختلف النوع الذي يرمز إليه بـ decltypeعن النوع الذي يستنتج بـ auto.
#include <vector>باستخدام std :: vector ;int main () { const vector < int > v ( 1 ); auto a = v [ 0 ]; // a من النوع int decltype ( v [ 0 ]) b = 1 ; // b من النوع const int&، وهو نوع الإرجاع لـ // std::vector<int>::operator[](size_type) const auto c = 0 ; // c من النوع int auto d = c ; // d من النوع int decltype ( c ) e ; // e من النوع int، وهو نوع الكيان المسمى بواسطة c decltype (( c )) f = c ; // f من النوع int&، لأن (c) قيمة يسارية decltype ( 0 ) g ; // g من النوع int، لأن 0 قيمة يمينية }حلقة تكرارية تعتمد على النطاق
يُوسّع C++11 صيغة العبارة forللسماح بالتكرار السهل على نطاق من العناصر:
int a [ 5 ] = { 1 , 2 , 3 , 4 , 5 }; // مضاعفة قيمة كل عنصر في a: for ( int & x : a ) { x *= 2 ; }// مشابه ولكن باستخدام استنتاج النوع لعناصر المصفوفة for ( auto & x : a ) { x *= 2 ; }هذا النوع من forحلقة التكرار، المسمى "حلقة التكرار النطاقية"، يُكرر على كل عنصر في القائمة. وهو يعمل مع المصفوفات المكتوبة بلغة C (شريطة معرفة طولها وقت الترجمة)، وقوائم التهيئة، وأي نوع يحتوي على begin()دوال end()تُعرّف له مُكرِّرات. كما أن جميع حاويات المكتبة القياسية التي تحتوي على أزواج بداية/نهاية تعمل مع حلقة التكرار النطاقية.
الدوال والتعبيرات اللامدا
تتيح لغة C++11 إمكانية إنشاء دوال مجهولة ، تُسمى دوال لامدا. [ 14 ] وتُعرَّف هذه الدوال كما يلي:
// تعريف دالة لامدا باسم add // تأخذ عددين صحيحين وتعيد عددًا صحيحًا auto add = []( int x , int y ) -> int { return x + y ; }يمكن حذف نوع القيمة المُعادة ( في هذا المثال) طالما أن جميع التعبيرات تُعيد نفس النوع. ويمكن أن تكون دالة لامدا اختيارياً دالة مغلقة .->intreturn
بناء جملة الدالة البديلة
كانت صيغة تعريف الدوال القياسية في لغة C كافية تمامًا لمجموعة ميزات هذه اللغة. ومع تطور لغة C++ من لغة C، حافظت على الصيغة الأساسية ووسّعتها عند الحاجة. إلا أنه مع ازدياد تعقيد C++، ظهرت بعض القيود، لا سيما فيما يتعلق بتعريف الدوال القالبية. على سبيل المثال، في C++03، هذا غير صحيح:
template < class L , class R > Ret add ( const L & lhs , const R & rhs ) { return lhs + rhs ; // يجب أن يكون Ret من نوع lhs + rhs }النوع هو ما ينتج Retعن جمع الأنواع . حتى مع وظائف C++11 المذكورة سابقًا ، فإن هذا غير ممكن.LRdecltype
// قالب C++11 غير صالح < class L , class R > decltype ( lhs + rhs ) add ( const L & lhs , const R & rhs ) { return lhs + rhs ; }هذا ليس كود C++ صالحًا لأن lhsو rhsلم يتم تعريفهما بعد؛ ولن يكونا معرفين صالحين إلا بعد أن يقوم المحلل اللغوي بتحليل بقية نموذج الدالة.
ولتجاوز هذه المشكلة، قدمت C++11 صيغة جديدة لإعلان الدالة، مع نوع إرجاع لاحق : [ 15 ]
template < class L , class R > auto add ( const L & lhs , const R & rhs ) -> decltype ( lhs + rhs ) { return lhs + rhs ; }يمكن استخدام هذا التركيب النحوي لتصريحات وتعريفات الدوال الأكثر اعتيادية:
struct X { static auto add ( int x , int y ) -> int { return x + y ; } };يُعد استخدام الكلمة autoالمفتاحية " " في هذه الحالة جزءًا من بناء الجملة فقط، ولا يُجري استنتاجًا تلقائيًا للنوع في C++11. مع ذلك، بدءًا من C++14، يمكن حذف نوع الإرجاع اللاحق بالكامل، وسيقوم المُصرّف باستنتاج نوع الإرجاع تلقائيًا. [ 16 ]
تحسين بناء الكائنات
في لغة C++03، لا يُسمح لدوال البناء في فئة ما باستدعاء دوال بناء أخرى في قائمة تهيئة تلك الفئة. يجب على كل دالة بناء أن تُنشئ جميع أعضاء فئتها بنفسها أو أن تستدعي دالة عضو مشتركة، كما يلي:
class Integer { private : int value ;void setValue ( int num ) { value = num ; } public : Integer ( int number ) { setValue ( number ); }عدد صحيح () { setValue ( 42 )؛ } };لا يمكن عرض دوال البناء الخاصة بالفئات الأساسية مباشرةً في الفئات المشتقة؛ إذ يجب على كل فئة مشتقة تنفيذ دوال البناء حتى لو كان من المناسب استخدام دالة بناء خاصة بالفئة الأساسية. ولا يمكن تهيئة عناصر البيانات غير الثابتة في الفئات عند تعريفها، وإنما فقط في دالة البناء.
يوفر C++11 حلولاً لجميع هذه المشاكل.
تسمح لغة C++11 للدوال البانية باستدعاء دوال بانية أخرى مماثلة (يُطلق عليها اسم التفويض ). وهذا يُمكّن الدوال البانية من الاستفادة من سلوك دالة بانية أخرى بأقل قدر من التعليمات البرمجية الإضافية. وقد استُخدم التفويض في لغات أخرى، مثل Java و Objective-C .
هذه الصيغة هي كالتالي:
class Integer { private : int value ; public : Integer ( int number ) : value ( number ) {}عدد صحيح () : عدد صحيح ( 42 ) {} };في هذه الحالة، كان من الممكن تحقيق نفس النتيجة بتحديد numberقيمة افتراضية للمعامل. مع ذلك، يسمح التركيب الجديد بتحديد القيمة الافتراضية (42) في التنفيذ بدلاً من الواجهة، وهو ما يُعدّ ميزةً لمطوري المكتبات البرمجية، حيث تُدمج القيم الافتراضية لمعاملات الدوال في مواقع الاستدعاء، بينما يسمح تفويض المُنشئ بتغيير القيمة دون الحاجة إلى إعادة تجميع الكود الذي يستخدم المكتبة.
مع ذلك، ثمة ملاحظة هامة: في C++03، يُعتبر الكائن مُنشأً عند انتهاء تنفيذ مُنشئه، بينما في C++11، يُعتبر الكائن مُنشأً بمجرد انتهاء تنفيذ أي مُنشئ. وبما أنه سيُسمح بتنفيذ مُنشئات متعددة، فهذا يعني أن كل مُنشئ مُفوِّض سيُنفَّذ على كائن مُنشأ بالكامل من نوعه. أما مُنشئات الفئات المُشتقة، فستُنفَّذ بعد اكتمال جميع عمليات التفويض في فئاتها الأساسية.
بالنسبة لمنشئات الفئة الأساسية، يسمح C++11 للفئة بتحديد وراثة منشئات الفئة الأساسية. وبالتالي، يُنشئ مُصرّف C++11 شيفرةً لتنفيذ الوراثة وتمرير الفئة المشتقة إلى الفئة الأساسية. هذه ميزة شاملة: إما أن تُمرَّر جميع منشئات الفئة الأساسية أو لا تُمرَّر أيٌّ منها. كذلك، يُخفى المنشئ الموروث إذا تطابق توقيعه مع توقيع منشئ الفئة المشتقة، وتوجد قيود على الوراثة المتعددة: لا يمكن وراثة منشئات الفئات من فئتين تستخدمان منشئات بنفس التوقيع .
الصيغة كالتالي:
class Base { public : Base ( int value ); };class Derived : public Base { public : using Base :: Base ; };فيما يخص تهيئة الأعضاء، يسمح C++11 بهذا التركيب النحوي:
class Integer { private : int value = 5 ; public : Integer () {}explicit Integer ( int value ) : value ( value ) {} };أي دالة إنشاء في الصنف ستُهيئ المتغير valueبالقيمة 5، ما لم تُعدّل دالة الإنشاء هذه القيمة بقيمتها الخاصة. لذا، ستُهيئ دالة الإنشاء الفارغة المذكورة أعلاه المتغير valueكما هو منصوص عليه في تعريف الصنف، بينما ستُهيئ دالة الإنشاء التي تأخذ قيمة عددية صحيحة المتغير بالقيمة المُعطاة.
ويمكنه أيضًا استخدام المُنشئ أو التهيئة الموحدة، بدلاً من تهيئة التعيين الموضحة أعلاه.
تجاوزات صريحة ونهائية
في لغة C++03، من الممكن إنشاء دالة افتراضية جديدة عن طريق الخطأ، عندما يكون الهدف هو إعادة تعريف دالة موجودة في الفئة الأساسية. على سبيل المثال:
struct Base { virtual void foo ( float x ); };struct Derived : public Base { virtual void foo ( int x ); };لنفترض أن Derived::foo()الغرض من هذه الدالة هو استبدال نسخة الفئة الأساسية. ولكن بدلاً من ذلك، ولأنها تحمل توقيعًا مختلفًا ، فإنها تُنشئ دالة افتراضية ثانية. هذه مشكلة شائعة، خاصةً عندما يُحاول المستخدم تعديل الفئة الأساسية.
يوفر C++11 صيغة لحل هذه المشكلة.
struct Base { virtual void foo ( float x ); };struct Derived : public Base { virtual void foo ( int x ) override ; // غير صحيح - لا يقوم بتجاوز طريقة الفئة الأساسية };يعني المعرّف overrideالخاص أن المترجم سيتحقق من الفئة (الفئات) الأساسية لمعرفة ما إذا كانت هناك دالة افتراضية بنفس التوقيع. وإذا لم تكن موجودة، فسيشير المترجم إلى وجود خطأ.
يُضيف C++11 أيضًا إمكانية منع الوراثة من الفئات أو ببساطة منع تجاوز الدوال في الفئات المشتقة. ويتم ذلك باستخدام المعرّف الخاص final. على سبيل المثال:
struct Base final { // ... };// غير صحيح لأن الفئة Base قد تم وضع علامة نهائية عليها struct Derived : public Base { // ... };struct Base { virtual void foo () final ; };struct Derived : public Base { void foo (); // غير صحيح لأن الدالة الافتراضية Base::foo تم وضع علامة final عليها };في هذا المثال، virtual void foo() final;يُعلن البيان عن دالة افتراضية جديدة، ولكنه يمنع أيضًا الفئات المشتقة من إعادة تعريفها. كما يمنع الفئات المشتقة من استخدام اسم الدالة ومجموعة المعاملات المحددة.
لا تُعدّ أيٌّ من overrideهاتين finalالكلمتين مفتاحًا لغويًا. فهما من الناحية التقنية مُعرِّفات لسمات المُصرِّح:
- تكتسب هذه السمات معنى خاصًا فقط عند استخدامها في تلك السياقات اللاحقة المحددة (بعد جميع محددات النوع، ومحددات الوصول، وتصريحات الأعضاء (لأنواع البنية والفئة والتعداد) ومحددات المُعلن، ولكن قبل تهيئة أو تنفيذ التعليمات البرمجية لكل مُعلن في قائمة مُعلنات مفصولة بفواصل)؛
- إنها لا تغير توقيع النوع المعلن ولا تعلن أو تتجاوز أي معرف جديد في أي نطاق؛
- قد يتم توسيع سمات المُعلن المعترف بها والمقبولة في الإصدارات المستقبلية من لغة C++ (بعض الامتدادات الخاصة بالمترجم تتعرف بالفعل على سمات المُعلن المضافة، لتوفير خيارات توليد التعليمات البرمجية أو تلميحات التحسين للمترجم، أو لتوليد بيانات إضافية في التعليمات البرمجية المُجمعة، والمخصصة لأدوات تصحيح الأخطاء، والروابط، ونشر التعليمات البرمجية المُجمعة، أو لتوفير سمات أمان خاصة بالنظام، أو لتعزيز قدرات البرمجة الانعكاسية (الانعكاس) في وقت التشغيل، أو لتوفير معلومات ربط إضافية للتوافق مع لغات البرمجة الأخرى وأنظمة وقت التشغيل؛ قد تأخذ هذه الامتدادات معلمات بين قوسين بعد مُعرف سمة المُعلن؛ وللتوافق مع معيار ANSI، يجب أن تستخدم هذه الامتدادات الخاصة بالمترجم اصطلاح البادئة المزدوجة).
- في أي مكان آخر، يمكن أن تكون هذه المعرفات صالحة للإعلانات الجديدة (واستخدامها لاحقًا إذا كانت متاحة).
ثابت ونوع المؤشر الفارغ
لأغراض هذا القسم فقط، 0يُقصد بكل ظهور لـ " " عبارة ثابتة تُقيّم إلى 0، وهي من النوع int. في الواقع، يمكن أن تكون العبارة الثابتة من أي نوع صحيح.
منذ ظهور لغة C عام 1972، 0أصبح للثابت دور مزدوج: عدد صحيح ثابت ومؤشر فارغ ثابت . تمت معالجة الغموض الكامن في المعنى المزدوج لـ 0في لغة C باستخدام ماكرو المعالج المسبق `< input type="null NULL">`، والذي يُوسّع عادةً إلى `<input type="null" >` أو ` <input type ="null">`. تمنع لغة C++ التحويل الضمني من `<input type="null" > ` إلى أنواع مؤشرات أخرى، مما يُلغي فائدة التحويل إلى `<input type="null ">`. ونتيجةً لذلك، يُسمح فقط بـ `<input type="null">` كمؤشر فارغ ثابت. يتعارض هذا بشكل سيئ مع تحميل الدوال الزائد .((void*)0)0void*0void*0
void foo ( char * s ); void foo ( int i );إذا NULLتم تعريفها على النحو التالي 0(وهو ما يحدث عادة في لغة C++)، فإن العبارة ستستدعي ، وهو أمر ليس ما قصده المبرمج على الأرجح، وليس ما توحي به القراءة السطحية للكود.foo(NULL);foo(inti)
يُصحح C++11 هذا الأمر بإضافة كلمة مفتاحية جديدة تُستخدم كثابت مؤشر فارغ مميز: `null` nullptr. وهي من النوع `null` nullptr_t، الذي يُمكن تحويله ضمنيًا ومقارنته بأي نوع مؤشر أو نوع مؤشر إلى عضو. ولا يُمكن تحويله ضمنيًا أو مقارنته بالأنواع الصحيحة، باستثناء `null` bool. بينما نصّ الاقتراح الأصلي على nullptr_tعدم إمكانية تحويل قيمة `null` من النوع `null` إلى `null` bool، قرر فريق عمل اللغة الأساسي أن هذا التحويل مرغوب فيه، من أجل التناسق مع أنواع المؤشرات العادية. وقد تمّ التصويت بالإجماع على التغييرات المقترحة في الصياغة وإدراجها في ورقة العمل في يونيو 2008.كما تم تقديم اقتراح مماثل إلى فريق عمل معيار C وتم قبوله لإدراجه في C23 . [ 17 ]
لأسباب تتعلق بالتوافق مع الإصدارات السابقة، 0يظل ثابت مؤشر فارغ صالحًا.
char * pc = nullptr ; // صحيح int * pi = nullptr ; // صحيح bool b = nullptr ; // صحيح. b خطأ. int i = nullptr ; // خطأfoo ( nullptr ); // يستدعي foo(nullptr_t p)، وليس foo(int i)؛ /* لاحظ أن foo(nullptr_t p) سيستدعي في الواقع foo(char* s) في المثال أعلاه باستخدام تحويل ضمني، فقط في حال عدم وجود دوال أخرى مُحمّلة بشكل زائد بأنواع مؤشرات متوافقة في النطاق. في حال وجود تحميلات زائدة متعددة، سيفشل الحل نظرًا للغموض، ما لم يكن هناك تعريف صريح لـ foo(nullptr_t p). في ملفات رأس الأنواع القياسية للغة C++11، يجب تعريف النوع nullptr_t على النحو التالي: using nullptr_t = decltype(nullptr); وليس على النحو التالي: using nullptr_t = int; // الإصدارات السابقة من C++ التي تتطلب تعريف NULL على أنه 0 using nullptr_t = void*; // معيار ANSI C الذي يُعرّف NULL على أنه ((void*)0) */تعدادات ذات أنواع محددة بدقة
في لغة C++03، لا تُعتبر التعدادات آمنة من حيث النوع. فهي تُعامل فعليًا كأعداد صحيحة، حتى عندما تكون أنواع التعداد مختلفة. وهذا يسمح بالمقارنة بين قيمتين من تعدادين مختلفين. الأمان الوحيد الذي توفره C++03 هو عدم تحويل عدد صحيح أو قيمة من نوع تعداد ما ضمنيًا إلى نوع تعداد آخر. علاوة على ذلك، فإن النوع الصحيح الأساسي مُحدد من قِبل التنفيذ؛ وبالتالي، فإن أي كود يعتمد على حجم التعداد يكون غير قابل للنقل. أخيرًا، تقتصر قيم التعداد على النطاق المحيط بها. لذا، لا يمكن أن يكون لتعدادين منفصلين في نفس النطاق أسماء أعضاء متطابقة.
يسمح معيار C++11 بتصنيف خاص للتعداد لا يعاني من أي من هذه المشكلات. ويتم التعبير عن ذلك باستخدام التصريح enum class( enum structيُقبل أيضًا كمرادف):
enum class CardSuit { CLUBS = 1 , HEARTS = 2 , DIAMONDS = 4 , SPADES = 8 };هذا التعداد آمن من حيث النوع. لا يتم تحويل قيم فئة التعداد ضمنيًا إلى أعداد صحيحة. وبالتالي، لا يمكن مقارنتها بالأعداد الصحيحة أيضًا ( يؤدي التعبير إلى خطأ في الترجمة).CardSuit::SPADES==8
النوع الأساسي لفئات التعداد معروف دائمًا. النوع الافتراضي هو `<enum>` int؛ ويمكن تغييره إلى نوع عددي مختلف كما هو موضح في هذا المثال:
enum class Direction : char { UP , DOWN , LEFT , RIGHT };في التعدادات التقليدية، تُوضع القيم في النطاق الخارجي. أما في التعدادات الحديثة، فتُوضع ضمن نطاق اسم فئة التعداد. لذا، في المثال أعلاه، UPتكون القيمة غير مُعرَّفة، بينما Direction::UPتكون القيمة مُعرَّفة.
يوجد أيضًا بناء جملة انتقالي يسمح للتعدادات القديمة بتوفير نطاق صريح، وتعريف النوع الأساسي:
enum Direction : char { UP , DOWN , LEFT , RIGHT };في هذه الحالة، يتم تعريف أسماء المعدِّد في نطاق التعداد ( Direction::UP)، ولكن من أجل التوافق مع الإصدارات السابقة، يتم وضعها أيضًا في النطاق المحيط.
أصبح تعريف أنواع التعداد (enums) مسبقًا ممكنًا في لغة C++11. سابقًا، لم يكن بالإمكان تعريف أنواع التعداد مسبقًا لأن حجم التعداد كان يعتمد على تعريف عناصره. أما الآن، فبمجرد تحديد حجم التعداد ضمنيًا أو صراحةً، يُمكن تعريفه مسبقًا.
enum Enum1 ; // غير صالح في C++03 وC++11؛ لا يمكن تحديد النوع الأساسي. enum Enum2 : unsigned int ; // صالح في C++11، تم تحديد النوع الأساسي بشكل صريح. enum class Enum3 ; // صالح في C++11، النوع الأساسي هو int. enum class Enum4 : unsigned int ; // صالح في C++11. enum Enum2 : unsigned short ; // غير صالح في C++11، لأن Enum2 تم تعريفه سابقًا بنوع أساسي مختلف.قوس الزاوية القائمة
يُعرّف محلل C++03 الرمز " " كعامل إزاحة لليمين أو عامل استخراج دفق في جميع الحالات. مع ذلك، عند استخدام تعريفات القوالب المتداخلة، يميل المبرمج إلى إهمال وضع مسافة بين القوسين الزاويين، مما يتسبب في خطأ في بناء الجملة من قِبل المُصرّف.>>
يُحسّن C++11 مواصفات المُحلِّل بحيث تُفسَّر الأقواس الزاوية المتعددة على أنها تُغلق قائمة وسائط القالب عند الضرورة. ويمكن تجاوز هذا باستخدام الأقواس حول تعبيرات المعاملات باستخدام المعاملات الثنائية " >" أو " >=" أو " ".>>
باستخدام std :: vector ;template < bool Test > class X ; vector < X < 1 > 2 >> x1 ; // يُفسَّر على أنه std::vector من X<true>، // متبوعًا بـ "2 >> x1"، وهي صيغة غير صحيحة للمُعرِّف. 1 تعني صحيح.vector < X < ( 1 > 2 ) >> x1 ; // يُفسَّر على أنه std::vector من النوع X<false>، // متبوعًا بالمُعرِّف "x1"، وهو صيغة صحيحة في C++11. (1>2) تعني خطأ.عوامل التحويل الصريحة
أضافت لغة C++98 الكلمة explicitالمفتاحية `constructors` كمُعدِّل على الدوال البانية لمنع استخدام الدوال البانية ذات الوسيط الواحد كمعاملات تحويل ضمنية للأنواع. مع ذلك، لا يُؤثر هذا على عوامل التحويل الفعلية. على سبيل المثال، قد تحتوي فئة المؤشر الذكي على `constructors` لتمكينها من العمل كمؤشر بدائي: إذا تضمنت هذا التحويل، فيمكن اختبارها باستخدام `constructors` (والتي ستكون صحيحة إذا كان المؤشر غير فارغ، وخاطئة في غير ذلك). مع ذلك، يسمح هذا بتحويلات أخرى غير مقصودة أيضًا. نظرًا لأن C++ مُعرَّفة كنوع حسابي، فيمكن تحويلها ضمنيًا إلى أنواع صحيحة أو حتى أنواع ذات فاصلة عائمة، مما يسمح بإجراء عمليات حسابية غير مقصودة من قِبل المستخدم.operatorbool()if(p)bool
في لغة C++11، explicitيُمكن الآن تطبيق الكلمة المفتاحية `bool` على عوامل التحويل. وكما هو الحال مع الدوال البانية، فإنها تمنع استخدام دوال التحويل هذه في التحويلات الضمنية. مع ذلك، تُعتبر سياقات اللغة التي تتطلب تحديدًا قيمة منطقية (شروط عبارات if والحلقات، ومعاملات عوامل التشغيل المنطقية) تحويلات صريحة، وبالتالي يُمكنها استخدام عامل تحويل منطقي.
على سبيل المثال، تحل هذه الميزة مشكلة القيم المنطقية الآمنة بشكل واضح .
أسماء مستعارة للقوالب
في لغة C++03، يُمكن تعريف نوع مُعرّف (typedef) كمرادف لنوع آخر فقط، بما في ذلك مرادف لتخصيص قالب مع تحديد جميع وسائط القالب الفعلية. لا يُمكن إنشاء قالب مُعرّف (typedef template). على سبيل المثال:
template < typename First , typename Second , int Third > class X ;template < typename Second > typedef X < A , Second , 5 > Y ; // غير صالح في C++03لن يتم تجميع هذا الكود.
تُضيف لغة C++11 هذه الإمكانية باستخدام هذه الصيغة:
template < typename First , typename Second , int Third > class X ;template < typename Second > using Y = X < A , Second , 5 > ;usingيمكن أيضًا استخدام هذا التركيب النحوي كاسم بديل للأنواع في C++11:
typedef void ( * FunctionType )( double ); // النمط القديم باستخدام FunctionType = void ( * )( double ); // الصيغة الجديدةالنقابات غير المقيدة
في لغة C++03، توجد قيود على أنواع الكائنات التي يمكن أن تكون أعضاءً في الاتحاد union. على سبيل المثال، لا يمكن أن تحتوي الاتحادات على أي كائنات تُعرّف مُنشئًا أو مُدمِّرًا غير تافه. وقد رفعت لغة C++11 بعض هذه القيود.
إذا كان لأحد الأعضاء دالة عضو خاصةunion غير تافهة ، فلن يقوم المترجم بإنشاء دالة العضو المكافئة له، ويجب تعريفها يدويًا.union
هذا مثال بسيط على عملية الاتحاد المسموح بها في لغة C++11:
#include <new> // مطلوب لوضع 'new'.struct Point { int a ; int b ;Point () {} Point ( int x , int y ) : x ( a ), y ( b ) {} };union MyUnion { int z ; double w ; Point p ; // غير صالح في C++03؛ صالح في C++11.// نظرًا لوجود عضو Point، يلزم الآن تعريف دالة البناء. MyUnion () {}MyUnion ( const Point & pt ) : p ( pt ) {} // إنشاء كائن Point باستخدام قائمة التهيئة.MyUnion & operator = ( const Point & pt ) { // إسناد كائن Point باستخدام الموضع 'new'. new ( & p ) Point ( pt ); return * this ; } };لن تؤدي التغييرات إلى تعطيل أي كود موجود لأنها تخفف فقط القواعد الحالية.
تحسينات في وظائف اللغة الأساسية
تتيح هذه الميزات للغة القيام بأشياء كانت مستحيلة في السابق، أو مطولة للغاية، أو تتطلب مكتبات غير قابلة للنقل.
قوالب متغيرة
في لغة C++11، يمكن للقوالب أن تأخذ أعدادًا متغيرة من معلمات القالب. وهذا يسمح أيضًا بتعريف الدوال ذات المعاملات المتغيرة الآمنة من حيث النوع .
سلاسل نصية جديدة
يُقدّم C++03 نوعين من السلاسل النصية . النوع الأول، الموجود بين علامتي اقتباس مزدوجتين، يُنتج مصفوفة منتهية بـ null من النوع const char. أما النوع الثاني، المُعرّف كـ L""، فيُنتج مصفوفة منتهية بـ null من النوع const wchar_t، حيث wchar_tحرف عريض ذو حجم ودلالات غير مُحدّدة. لا يدعم أيٌّ من النوعين السلاسل النصية المُشفّرة بترميز UTF-8 أو UTF-16 أو أي نوع آخر من ترميزات Unicode .
يدعم C++11 ثلاثة ترميزات Unicode: UTF-8 وUTF-16 و UTF-32 . تم تعديل تعريف النوع charليُوضح صراحةً أنه على الأقل بالحجم اللازم لتخزين ترميز UTF-8 ذي 8 بتات، وكبير بما يكفي لاحتواء أي عنصر من مجموعة أحرف التنفيذ الأساسية للمُصرّف. كان يُعرَّف سابقًا على أنه الأخير فقط في معيار C++ نفسه، معتمدًا آنذاك على معيار C لضمان 8 بتات على الأقل. علاوة على ذلك، يُضيف C++11 نوعين جديدين من الأحرف: UTF-8 char16_tو char32_tUTF-16. صُمِّم هذان النوعان لتخزين UTF-16 وUTF-32 على التوالي.
يمكن إنشاء سلاسل نصية حرفية لكل ترميز مدعوم على النحو التالي:
u8 "أنا سلسلة UTF-8." u "هذه سلسلة UTF-16." U "هذه سلسلة UTF-32."نوع السلسلة الأولى هو النوع المعتاد const char[]. نوع السلسلة الثانية هو const char16_t[](لاحظ البادئة 'u' الصغيرة). نوع السلسلة الثالثة هو const char32_t[](البادئة 'U' الكبيرة).
عند إنشاء سلاسل نصية حرفية باستخدام ترميز يونيكود، من المفيد غالبًا إدراج نقاط ترميز يونيكود مباشرةً في السلسلة. وللقيام بذلك، يسمح C++11 بهذه الصيغة:
u8 "هذا حرف يونيكود: \u2018 ." u "هذا حرف يونيكود أكبر: \u2018 ." U "هذا حرف يونيكود: \U00002018 ."الرقم الذي يلي الرمز \uهو رقم سداسي عشري، ولا يحتاج إلى 0xالبادئة المعتادة. يُمثل المعرّف \uنقطة ترميز يونيكود ذات 16 بت؛ لإدخال نقطة ترميز ذات 32 بت، استخدم \Uالرمز ورقمًا سداسيًا عشريًا ذا 32 بت. لا يُمكن إدخال سوى نقاط ترميز يونيكود الصالحة. على سبيل المثال، نقاط الترميز في النطاق U+D800–U+DFFF ممنوعة، لأنها محجوزة لأزواج بديلة في ترميزات UTF-16.
من المفيد أحيانًا تجنب تهريب السلاسل النصية يدويًا، خاصةً عند استخدام القيم الحرفية لملفات XML أو لغات البرمجة النصية أو التعابير النمطية. يوفر C++11 سلسلة نصية خام:
R " ( The String Data \ Stuff " ) " R " delimiter( The String Data \ Stuff " )delimiter "في الحالة الأولى، كل ما بين "(و )"يُعتبر جزءًا من السلسلة النصية. لا حاجة إلى وضع علامة هروب قبل الحرفين و . في الحالة الثانية، يبدأ "الحرف السلسلة النصية، وينتهي فقط عند الوصول إلى . يمكن أن تكون السلسلة النصية أي سلسلة يصل طولها إلى 16 حرفًا، بما في ذلك السلسلة الفارغة. لا يمكن أن تحتوي هذه السلسلة على مسافات أو أحرف تحكم أو أو أو الحرف . باستخدام سلسلة الفاصل هذه، يمكن للمستخدم الحصول على التسلسل داخل السلاسل النصية الخام. على سبيل المثال، يُعادل .\"delimiter()delimiter"delimiter()\)"R"delimiter("(a-z)")delimiter""\"(a-z)\""
يمكن دمج السلاسل النصية الخام مع البادئة العريضة أو أي من بادئات اليونيكود الحرفية:
u8R " XXX( أنا سلسلة "UTF-8" خام. )XXX " uR " *( هذه سلسلة "UTF-16" خام. )* " UR " ( هذه سلسلة "UTF-32" خام. ) "القيم الحرفية التي يحددها المستخدم
توفر لغة C++03 عددًا من القيم الحرفية. تُعدّ الأحرف 12.5قيمة حرفية يُحللها المُصرّف كنوع بيانات doubleبقيمة 12.5. مع ذلك، فإن إضافة اللاحقة f، كما في المثال 12.5f، تُنشئ قيمة من النوع floatالذي يحتوي على القيمة 12.5. تُحدد مواصفات C++ مُعدِّلات اللواحق للقيم الحرفية، ولا يُمكن لشيفرة C++03 إنشاء مُعدِّلات جديدة للقيم الحرفية.
وعلى النقيض من ذلك، فإن C++11 تُمكّن المستخدم من تعريف أنواع جديدة من المُعدِّلات الحرفية التي ستُنشئ كائنات بناءً على سلسلة الأحرف التي يُعدِّلها الحرف.
يُعاد تعريف تحويل القيم الحرفية إلى مرحلتين متميزتين: الخام والمعالج. القيمة الحرفية الخام هي سلسلة من الأحرف من نوع محدد، بينما القيمة الحرفية المعالجة من نوع مختلف. القيمة الحرفية في لغة C++ ، كقيمة حرفية خام ، 1234هي هذه السلسلة من الأحرف '1'1234. أما كقيمة حرفية معالجة، فهي العدد الصحيح 1234. القيمة الحرفية في لغة C++ في صورتها الخام هي 1234 ، بينما في صورتها المعالجة هي العدد الصحيح 10.'2''3''4'0xA'0''x''A'
يمكن توسيع القيم الحرفية في كل من صورتها الأصلية والمُعالجة، باستثناء القيم النصية الحرفية التي لا يمكن معالجتها إلا في صورتها المُعالجة. ويعود هذا الاستثناء إلى أن السلاسل النصية تحتوي على بادئات تؤثر على المعنى المحدد ونوع الأحرف المعنية.
جميع القيم الحرفية المُعرَّفة من قِبل المستخدم هي لواحق؛ ولا يُمكن تعريف قيم حرفية بادئة. جميع اللواحق التي تبدأ بأي حرف باستثناء الشرطة السفلية ( __) محجوزة وفقًا للمعيار. لذا، يجب أن تحتوي جميع القيم الحرفية المُعرَّفة من قِبل المستخدم على لواحق تبدأ بشرطة سفلية ( __). [ 18 ]
يتم تعريف القيم الحرفية التي يُحددها المستخدم والتي تعالج الشكل الخام للقيمة الحرفية عبر عامل تشغيل حرفي، والذي يُكتب على النحو التالي operator"": . فيما يلي مثال:
MyOutput operator "" _suf ( const char * s ) { // يفترض أن MyOutput يحتوي على مُنشئ يأخذ const char* MyOutput ret ( s ); return ret ; }MyOutput x = 1234_suf ; // بافتراض أن MyOutput يحتوي على دالة value() تُرجع قيمة من نوع double assert ( x.value ( ) == 1234.0 )تُنفّذ عبارة الإسناد الكود المُعرّف بواسطة الدالة الحرفية المُعرّفة من قِبل المستخدم. تُمرّر هذه الدالة كسلسلة نصية على نمط لغة C، لذا فهي تحتوي على مُنهي سلسلة فارغ.MyOutputx=1234_suf;"1234"
تتمثل الآلية البديلة لمعالجة القيم الحرفية الأولية للأعداد الصحيحة والأعداد العشرية في استخدام قالب متغير الوسائط :
template < char ... _ > MyOutput operator "" _suf ();MyOutput x = 1234 _suf ; MyOutput y = 2.17 _suf ;يُنشئ هذا الأمر دالة معالجة النص الحرفي على النحو التالي . في هذه الصيغة، لا يوجد حرف فارغ يُنهي السلسلة. والهدف الرئيسي من ذلك هو استخدام الكلمة المفتاحية `constexpr` في C++11 لضمان أن يقوم المُصرّف بتحويل النص الحرفي بالكامل في وقت الترجمة، بافتراض أن `constexpr` نوع قابل للإنشاء والنسخ، وأن دالة معالجة النص الحرفي هي دالة.operator""_suf<'1','2','3','4'>()constexprMyOutputconstexpr
بالنسبة للقيم العددية، يكون نوع القيمة المُعالجة إما unsigned long longللقيم الصحيحة أو long doubleللقيم العشرية. (ملاحظة: لا حاجة لأنواع الأعداد الصحيحة المُوقّعة لأن القيمة المُسبقة بالإشارة تُحلل كتعبير يحتوي على الإشارة كعامل بادئة أحادي والعدد غير المُوقّع). لا يوجد شكل قالب بديل.
MyOutput operator "" _suf ( unsigned long long ); MyOutput operator "" _suf ( long double );MyOutput x = 1234 _suf ; // يستخدم التحميل الزائد 'unsigned long long'. MyOutput y = 3.1416 _suf ; // يستخدم التحميل الزائد 'long double'.وفقًا لبادئات السلاسل الجديدة المذكورة سابقًا، تُستخدم هذه البادئات للسلاسل النصية الحرفية:
MyOutput operator "" _suf ( const char * s , size_t length ); MyOutput operator "" _suf ( const wchar_t * s , size_t length ); MyOutput operator "" _suf ( const char16_t * s , size_t length ); MyOutput operator "" _suf ( const char32_t * s , size_t length );MyOutput a = "1234" _suf ; // يستخدم التحميل الزائد للدالة 'const char*'. MyOutput b = u8 "1234" _suf ; // يستخدم التحميل الزائد للدالة 'const char*'. MyOutput c = L "1234" _suf ; // يستخدم التحميل الزائد للدالة 'const wchar_t*'. MyOutput d = u "1234" _suf ; // يستخدم التحميل الزائد للدالة 'const char16_t*'. MyOutput e = U "1234" _suf ; // يستخدم التحميل الزائد للدالة 'const char32_t*'.لا يوجد نموذج قالب بديل. يتم تعريف الأحرف الحرفية بشكل مماثل.
نموذج الذاكرة متعدد الخيوط
تُوحّد لغة C++11 دعم البرمجة متعددة الخيوط .
يتضمن ذلك جزأين: نموذج ذاكرة يسمح بتواجد عدة خيوط في البرنامج، ودعم مكتبة للتفاعل بين هذه الخيوط. (انظر قسم إمكانيات الخيوط في هذه المقالة ).
يحدد نموذج الذاكرة متى يمكن لعدة خيوط الوصول إلى نفس موقع الذاكرة، ويحدد متى تصبح التحديثات التي يقوم بها خيط واحد مرئية للخيوط الأخرى.
التخزين المحلي للخيط
في بيئة متعددة الخيوط، من الشائع أن يكون لكل خيط بعض المتغيرات الفريدة . يحدث هذا بالفعل بالنسبة للمتغيرات المحلية للدالة، ولكنه لا يحدث بالنسبة للمتغيرات العامة والثابتة.
يتم تحديد مدة تخزين جديدة خاصة بالخيط (بالإضافة إلى المدة الثابتة والديناميكية والتلقائية الموجودة ) بواسطة مُحدد التخزين thread_local.
يمكن منح أي كائن ذي مدة تخزين ثابتة (أي، عمر يمتد طوال مدة تنفيذ البرنامج) مدة تخزين محلية خاصة بالخيط. والهدف من ذلك هو أنه، كما هو الحال مع أي متغير ذي مدة ثابتة، يمكن تهيئة الكائن المحلي الخاص بالخيط باستخدام دالة البناء وتدميره باستخدام دالة التدمير.
دوال الأعضاء الخاصة ذات القيم الافتراضية الصريحة
في لغة C++03، يُوفّر المُصرّف، للفئات التي لا تُوفّرها بنفسها، مُنشئًا افتراضيًا، ومُنشئ نسخ، ومعامل إسناد نسخ operator=، ومُدمّرًا. يُمكن للمُبرمج تجاوز هذه الإعدادات الافتراضية بتعريف نسخ مُخصصة. كما تُعرّف لغة C++ أيضًا العديد من المعاملات العامة (مثل `require` operator new) التي تعمل على جميع الفئات، والتي يُمكن للمُبرمج تجاوزها.
مع ذلك، لا توجد سيطرة تُذكر على إنشاء هذه الإعدادات الافتراضية. على سبيل المثال، يمكن جعل فئة ما غير قابلة للنسخ بطبيعتها عن طريق تعريف مُنشئ نسخ خاص وعامل إسناد نسخ دون تعريفهما. يُعدّ استخدام هذه الدوال انتهاكًا لقاعدة التعريف الواحد (ODR). ورغم عدم اشتراط ظهور رسالة تشخيصية، [ 19 ] فإنّ الانتهاكات قد تؤدي إلى خطأ في الرابط.
في حالة المُنشئ الافتراضي، لن يُنشئ المُصرّف مُنشئًا افتراضيًا إذا تم تعريف فئة ما باستخدام أي مُنشئات. يُعد هذا مفيدًا في كثير من الحالات، ولكن من المفيد أيضًا أن يكون بالإمكان استخدام كلٍ من المُنشئات المُخصصة والمُنشئ الافتراضي الذي يُنشئه المُصرّف.
يسمح C++11 بتحديد القيم الافتراضية وحذف هذه الدوال الأعضاء الخاصة بشكل صريح. [ 20 ] على سبيل المثال، تُصرّح هذه الفئة صراحةً بإمكانية استخدام مُنشئ افتراضي:
class X { X () = default ; // تم تحديد المُنشئ الافتراضي بشكل صريح. X ( T value ); };الوظائف المحذوفة صراحةً
يمكن تعطيل دالة بشكل صريح. وهذا مفيد لمنع التحويلات الضمنية للأنواع. = deleteيمكن استخدام المُحدِّد لمنع استدعاء دالة بأنواع مُعاملات مُحدَّدة. [ 20 ] على سبيل المثال:
void noInt ( double i ); void noInt ( int ) = delete ;سيرفض المترجم محاولة استدعاء الدالة noInt()باستخدام مُعامل، بدلاً من إجراء تحويل صامت إلى . لكن الاستدعاء باستخدام مُعامل آخر لا يزال يعمل.intdoublenoInt()float
من الممكن منع استدعاء الدالة بأي نوع آخر غير doubleاستخدام قالب:
double onlyDouble ( double d ) { return d ; }template < typename T > double onlyDouble ( T ) = delete ;سيؤدي استدعاء الدالة onlyDouble(1.0)إلى نجاح العملية، بينما onlyDouble(1.0f)سيؤدي استدعاء الدالة الأخرى إلى ظهور خطأ في المُصرّف.
يمكن أيضًا حذف دوال الأعضاء ودوال البناء الخاصة بالفئة. على سبيل المثال، من الممكن منع نسخ كائنات الفئة عن طريق حذف دالة البناء الخاصة بالنسخ و operator =:
class NonCopyable { NonCopyable (); NonCopyable ( const NonCopyable & ) = delete ; NonCopyable & operator = ( const NonCopyable & ) = delete ; };يكتبlong long int
في لغة C++03، كان أكبر نوع بيانات صحيح هو ` long intn`. وهو مضمون أن يحتوي على عدد بتات قابلة للاستخدام لا يقل عن عدد بتات `n` int. وقد نتج عن ذلك أن long intحجم `n` كان 64 بت في بعض التطبيقات الشائعة و32 بت في تطبيقات أخرى. أضافت لغة C++11 نوع بيانات صحيح جديدًا long long intلمعالجة هذه المشكلة. وهو مضمون أن يكون حجمه على الأقل مساويًا لحجم `n` long int، وأن يحتوي على 64 بت على الأقل. تم تقديم هذا النوع في الأصل بواسطة C99 إلى لغة C القياسية، وكانت معظم مترجمات C++ تدعمه بالفعل كامتداد. [ 21 ] [ 22 ]
التأكيدات الثابتة
توفر لغة C++03 طريقتين لاختبار التأكيدات : الماكرو assertوتوجيه المعالج المسبق #error. مع ذلك، لا يُناسب أيٌّ منهما الاستخدام في القوالب: يختبر الماكرو التأكيد أثناء التنفيذ، بينما يختبر توجيه المعالج المسبق التأكيد أثناء المعالجة المسبقة، التي تحدث قبل إنشاء القوالب. كما لا يُناسب أيٌّ منهما اختبار الخصائص التي تعتمد على معلمات القالب.
تُقدّم الأداة الجديدة طريقةً جديدةً لاختبار التأكيدات في وقت الترجمة، باستخدام الكلمة المفتاحية الجديدة static_assert. ويأخذ التصريح الشكل التالي:
static_assert( constant-expression , error-message );
فيما يلي بعض الأمثلة على كيفية static_assertاستخدامها:
static_assert (( GREEK_PI > 3.14 ) && ( GREEK_PI < 3.15 ), "GREEK_PI غير دقيق!" );template < class T > struct Check { static_assert ( sizeof ( int ) <= sizeof ( T ), "T ليس كبيرًا بما يكفي!" ); };باستخدام std :: is_integral ;template < class Int > Int foo ( Int x , Int y ) { static_assert ( is_integral < Int >:: value , "يجب أن يكون مُعامل foo() من النوع الصحيح." ); }عندما يكون التعبير الثابت غير صحيح، falseيُصدر المُصرّف رسالة خطأ. يُشبه المثال الأول توجيه المُعالِج المُسبق #error، مع أن المُعالِج المُسبق يدعم فقط الأنواع الصحيحة. [ 23 ] في المقابل، في المثال الثاني، يتم التحقق من التأكيد عند كل إنشاء لفئة القالب Check.
تُعدّ التأكيدات الثابتة مفيدةً حتى خارج القوالب. على سبيل المثال، قد يعتمد تطبيقٌ مُعيّنٌ لخوارزميةٍ ما على أن يكون حجمُ مُتغيرٍ ما long longأكبرَ من حجمِ مُتغيرٍ آخر int، وهو أمرٌ لا يضمنه المعيار. هذا الافتراض صحيحٌ في مُعظم الأنظمة والمُترجمات، ولكن ليس جميعها.
السماح sizeofبالعمل على أعضاء الفئات بدون كائن صريح
في لغة C++03، sizeofيمكن استخدام المعامل على الأنواع والكائنات. لكن لا يمكن استخدامه للقيام بما يلي:
struct X { T member ; };sizeof ( X :: member ); // لا يعمل مع C++03. يعمل مع C++11يفترض أن تُعيد هذه الدالة حجم المتغير T. لكن C++03 لا تسمح بذلك، لذا يُعتبر هذا خطأً في الترجمة. بينما تسمح به C++11. كما أنها مسموحة أيضاً بالنسبة alignofللمعامل الذي تم تقديمه في C++11.
التحكم في محاذاة الكائنات والاستعلام عنها
يسمح C++11 بالاستعلام عن محاذاة المتغيرات والتحكم فيها باستخدام alignofو alignas.
يأخذ هذا alignofالمعامل النوع ويعيد حد البايتات الذي يجب تخصيص مثيلات النوع عليه (كـ std::size_t). عند إعطائه نوعًا مرجعيًا، alignofفإنه يعيد محاذاة النوع المرجعي؛ أما بالنسبة للمصفوفات، فإنه يعيد محاذاة نوع العنصر.
يتحكم المُحدِّد alignasفي محاذاة الذاكرة لمتغير. يأخذ المُحدِّد قيمة ثابتة أو نوعًا؛ وعند تحديد نوع، يكون اختصارًا لـ . على سبيل المثال، لتحديد أن a يجب أن تكون محاذية بشكل صحيح لاحتواء a :alignas(T)alignas(alignof(T))char[]float
alignas ( float ) unsigned char c [ sizeof ( float )];السماح بتنفيذ عمليات جمع البيانات المهملة
نصّت معايير لغة C++ السابقة على إمكانية جمع البيانات المهملة يدويًا من قِبل المبرمج، لكنها لم تُعرّف إمكانية الوصول إلى الكائنات لأغراض جمع البيانات المهملة تلقائيًا. يُحدد معيار C++11 الشروط التي بموجبها تُشتق قيم المؤشرات "بأمان" من قيم أخرى. قد يُحدد التطبيق أنه يعمل وفقًا لمعايير أمان المؤشرات الصارمة ، وفي هذه الحالة، قد تصبح المؤشرات التي لا تُشتق وفقًا لهذه القواعد غير صالحة. وقد أُزيل هذا لاحقًا في معيار C++23 . [ 24 ]
صفات
توفر لغة C++11 صيغةً موحدةً لتوسيعات المُصرّف/الأدوات للغة. كانت هذه التوسيعات تُحدد تقليديًا باستخدام #pragmaتوجيهات أو كلمات مفتاحية خاصة بالمورد (مثل __attribute__GNU و __declspecMicrosoft). مع الصيغة الجديدة، يُمكن تحديد المعلومات الإضافية على شكل سمة مُحاطة بأقواس مربعة مزدوجة. يُمكن تطبيق السمة على عناصر مختلفة من شفرة المصدر.
int [[ attr1 ]] i [[ attr2 , attr3 ]];[[ attr4 ( arg1 , arg2 )]] if ( cond ) { [[ vendor :: attr5 ]] return i ; }في المثال أعلاه، ينطبق السمة على نوع المتغير ، وينطبق على المتغير نفسه، وينطبق على العبارة، وينطبق على عبارة الإرجاع. بشكل عام (مع بعض الاستثناءات)، توضع السمة المحددة لكيان مُسمى بعد الاسم، وقبل الكيان. بخلاف ذلك، كما هو موضح أعلاه، يمكن سرد عدة سمات داخل زوج واحد من الأقواس المربعة المزدوجة، ويمكن توفير وسائط إضافية لسمة ما، ويمكن تحديد نطاق السمات بواسطة مساحات أسماء سمات خاصة بالمورد.[[attr1]]i[[attr2]][[attr3]][[attr4]]if[[vendor::attr5]]
يُوصى بأن لا تحمل السمات أي دلالة لغوية، وألا تُغيّر من معنى البرنامج عند تجاهلها. يمكن أن تكون السمات مفيدة لتوفير معلومات تُساعد المُصرّف، على سبيل المثال، في إصدار تشخيصات أفضل أو تحسين الكود المُولّد.
يوفر C++11 سمتين قياسيتين: لتحديد أن الدالة لا تُرجع قيمة، وللمساعدة في تحسين التعليمات البرمجية متعددة الخيوط من خلال الإشارة إلى أن وسائط الدالة أو القيمة المرجعة تحمل تبعية.[[noreturn]][[carries_dependency]]
تغييرات مكتبة C++ القياسية
تم إدخال عدد من الميزات الجديدة في مكتبة C++11 القياسية. كان من الممكن تنفيذ العديد من هذه الميزات باستخدام المعيار القديم، لكن بعضها يعتمد (بدرجة أكبر أو أقل) على الميزات الأساسية الجديدة في C++11.
تم تعريف جزء كبير من المكتبات الجديدة في وثيقة التقرير الفني للمكتبات الصادر عن لجنة معايير لغة C++ (المسمى TR1)، والذي نُشر عام 2005. تتوفر حاليًا تطبيقات كاملة وجزئية متنوعة لـ TR1 باستخدام مساحة الاسم std::tr1. بالنسبة لـ C++11، نُقلت هذه التطبيقات إلى مساحة الاسم std. ومع ذلك، مع دمج ميزات TR1 في مكتبة C++11 القياسية، جرى تحديثها، عند الاقتضاء، بميزات لغة C++11 التي لم تكن متوفرة في الإصدار الأولي من TR1. كما يُحتمل أنها أُضيفت إليها ميزات كانت متاحة في C++03، ولكنها لم تكن جزءًا من مواصفات TR1 الأصلية.
تحديثات لمكونات المكتبة القياسية
يُقدّم C++11 عددًا من ميزات اللغة الجديدة التي يُمكن لمكونات المكتبة القياسية الحالية الاستفادة منها. على سبيل المثال، يُمكن لمعظم حاويات المكتبة القياسية الاستفادة من دعم مُنشئ النقل القائم على مرجع القيمة اليمنى، وذلك لنقل الحاويات الكبيرة بسرعة، ونقل محتويات تلك الحاويات إلى مواقع ذاكرة جديدة. وقد تمّ تحديث مكونات المكتبة القياسية بميزات لغة C++11 الجديدة عند الاقتضاء. وتشمل هذه الميزات، على سبيل المثال لا الحصر:
- مراجع القيمة اليمنى ودعم النقل المرتبط بها
- يدعم وحدات ترميز UTF-16 ووحدات ترميز UTF-32 وأنواع أحرف Unicode
- قوالب متغيرة (مقترنة بمراجع القيمة اليمنى للسماح بإعادة توجيه مثالية)
- تعبيرات الثوابت في وقت الترجمة
decltypeexplicitعوامل التحويل- تم تعريف الدوال على أنها افتراضية أو محذوفة
علاوة على ذلك، فقد مرّ وقت طويل منذ إصدار معيار C++ السابق، وكُتبت العديد من الأكواد باستخدام المكتبة القياسية. وقد كشف هذا عن جوانب في المكتبات القياسية تحتاج إلى تحسين. ومن بين مجالات التحسين العديدة التي نُظر فيها، مُخصِّصات الذاكرة في المكتبة القياسية . وقد أُدرج نموذج جديد لمُخصِّصات الذاكرة قائم على النطاق في C++11 لتكملة النموذج السابق.
مرافق تشكيل الخيوط
بينما توفر لغة C++03 نموذج ذاكرة يدعم الترابط، فإن الدعم الأساسي لاستخدام الترابط الفعلي يأتي مع مكتبة C++11 القياسية.
std::threadتُوفّر فئة الخيوط ( ) التي تأخذ كائن دالة (وسلسلة اختيارية من الوسائط لتمريرها إليها) لتشغيلها في الخيط الجديد. من الممكن إيقاف الخيط مؤقتًا حتى يكتمل تنفيذ خيط آخر، مما يوفر دعمًا لربط الخيوط عبر الدالة std::thread::join()العضوية. كما تُوفّر الدالة العضوية إمكانية الوصول، حيثما أمكن، إلى كائن (كائنات) الخيط الأصلي الأساسي لإجراء عمليات خاصة بالمنصةstd::thread::native_handle() .
لتحقيق التزامن بين الخيوط، تُضاف إلى المكتبة مؤشرات التزامن المناسبة ( مثل `mutex`std::mutex و` std::recursive_mutexmutex` و`mutex`) ومتغيرات الحالة ( مثل `mutex` std::condition_variableو`mutex` ). ويمكن الوصول إليها عبر أقفال RAII (مثل `mutex` و`mutex` ) وخوارزميات القفل لتسهيل الاستخدام.std::condition_variable_anystd::lock_guardstd::unique_lock
في العمليات عالية الأداء ومنخفضة المستوى، قد يكون التواصل بين الخيوط ضروريًا أحيانًا دون الحاجة إلى استخدام الأقفال المتبادلة. ويتم ذلك باستخدام عمليات ذرية على مواقع الذاكرة، والتي يمكن تحديد الحد الأدنى من قيود رؤية الذاكرة اللازمة للعملية. كما يمكن استخدام حواجز الذاكرة الصريحة لهذا الغرض.
تتضمن مكتبة الخيوط في C++11 أيضًا تقنيات المستقبلات والوعود لتمرير النتائج غير المتزامنة بين الخيوط، std::packaged_taskولتغليف استدعاء دالة يمكنها توليد مثل هذه النتيجة غير المتزامنة. وقد وُجهت انتقادات لاقتراح المستقبلات لافتقاره إلى طريقة لدمج المستقبلات والتحقق من اكتمال وعد واحد داخل مجموعة من الوعود. [ 25 ]
أُحيلت تفاصيل أخرى متعلقة بالمعالجة المتعددة عالية المستوى، مثل مجموعات الخيوط، إلى تقرير تقني لاحق خاص بلغة C++ . وهي ليست جزءًا من C++11، ولكن من المتوقع أن يُبنى تنفيذها النهائي بالكامل على ميزات مكتبة الخيوط.
تُوفر هذه الخاصية الجديدة std::asyncطريقةً مُريحةً لتشغيل المهام وربطها بـ std::future. يُمكن للمستخدم اختيار ما إذا كانت المهمة ستُشغّل بشكل غير متزامن على خيط منفصل أو بشكل متزامن على خيط ينتظر القيمة. بشكل افتراضي، يُمكن للتطبيق اختيار ، مما يُسهّل الاستفادة من التزامن في الأجهزة دون تجاوز الحد المسموح به، ويُوفر بعض مزايا مجمع الخيوط للاستخدامات البسيطة.
أنواع الصفوف
تُعدّ المجموعات المتسلسلة (tuples) عبارة عن مجموعات تتكون من كائنات غير متجانسة ذات أبعاد محددة مسبقًا. ويمكن اعتبار المجموعة المتسلسلة تعميمًا لمتغيرات أعضاء البنية (struct).
استفاد إصدار C++11 من نوع البيانات TR1 من ميزات C++11 مثل القوالب المتغيرة الوسائط . ولتنفيذه بكفاءة، تطلب إصدار TR1 تحديد عدد أقصى للأنواع المضمنة من قِبل المُنفِّذ، بالإضافة إلى استخدام مكثف لتقنيات الماكرو. في المقابل، لا يتطلب تنفيذ إصدار C++11 تحديدًا صريحًا لعدد أقصى للأنواع. مع أن المُصرِّفات تحتوي على حد أقصى داخلي لعمق الاستدعاء الذاتي لإنشاء القوالب (وهو أمر طبيعي)، إلا أن إصدار C++11 من البيانات لا يُظهر هذه القيمة للمستخدم.
باستخدام القوالب المتغيرة ، يبدو تعريف فئة tuple كما يلي:
namespace std { template < class ... Ts > class tuple ; }مثال على تعريف واستخدام نوع البيانات tuple:
باستخدام Types = std :: tuple < int , double , long & , const char *> ; long lengthy = 12 ; Types proof ( 18 , 6.5 , lengthy , "Ciao!" );lengthy = std :: get <0> ( profo ); // إسناد القيمة 18 إلى 'lengthy'. std :: get <3> ( profo ) = " جميل!" ; // تعديل العنصر الرابع من المجموعة.يمكن إنشاء المجموعة proofدون تحديد محتوياتها، ولكن فقط إذا كانت أنواع عناصر المجموعة تحتوي على مُنشئات افتراضية. علاوة على ذلك، يمكن إسناد مجموعة إلى مجموعة أخرى: إذا كانت أنواع المجموعتين متطابقة، فيجب أن يحتوي كل نوع عنصر على مُنشئ نسخ؛ وإلا، فيجب أن يكون كل نوع عنصر في المجموعة اليمنى قابلاً للتحويل إلى نوع العنصر المقابل في المجموعة اليسرى، أو أن يحتوي نوع العنصر المقابل في المجموعة اليسرى على مُنشئ مناسب.
باستخدام std :: string ؛ باستخدام T1 = std :: tuple < int , double , string > ؛ باستخدام T2 = std :: tuple < char , short , const char *> ؛T1 t1 ; T2 t2 ( 'X' , 2 , "Hola!" ); t1 = t2 ; // حسنًا، يمكن تحويل أول عنصرين، // ويمكن إنشاء العنصر الثالث من 'const char *'.كما هو الحال std::make_pairمع std::pair، توجد std::make_tupleأداة لإنشاء std::tuples تلقائيًا باستخدام استنتاج النوع، autoوتساعد في تعريف مثل هذه المجموعة. std::tieتُنشئ هذه الأداة مجموعات من مراجع القيم اليسرى للمساعدة في فك المجموعات. std::ignoreكما أنها مفيدة هنا أيضًا. انظر المثال:
#include <string> #include <tuple>باستخدام std :: string ;auto record = std :: make_tuple ( "Hari Ram" , "New Delhi" , 3.5 , 'A' ); string name ; float gpa ; char grade ; std :: tie ( name , std :: ignore , gpa , grade ) = record ; // std::ignore يُساعد في حذف اسم المكان std :: cout << name << ' ' << gpa << ' ' << grade << std :: endl ;تتوفر عوامل المقارنة (بين الصفوف التي تحتوي على نفس عدد العناصر)، ويتوفر تعبيران للتحقق من خصائص الصف (فقط أثناء الترجمة):
std::tuple_size<T>::valueتُعيد عدد العناصر في المجموعةT،std::tuple_element<I, T>::typeيُعيد نوع الكائن رقمIالصفT.
جداول التجزئة
يُعدّ تضمين جداول التجزئة (حاويات ترابطية غير مرتبة) في مكتبة C++ القياسية أحد أكثر المطالب تكرارًا. لم يتم اعتمادها في C++03 بسبب ضيق الوقت فقط. على الرغم من أن جداول التجزئة أقل كفاءة من الشجرة المتوازنة في أسوأ الحالات (في حال وجود العديد من التصادمات)، إلا أنها تُؤدي أداءً أفضل في العديد من التطبيقات العملية.
تُدار حالات التصادم فقط عبر التسلسل الخطي، لأن اللجنة لم ترَ من المناسب توحيد حلول العنونة المفتوحة التي تُسبب العديد من المشاكل الجوهرية (خاصةً عند السماح بحذف العناصر). ولتجنب تضارب الأسماء مع المكتبات غير القياسية التي طورت تطبيقاتها الخاصة لجداول التجزئة، استُخدمت البادئة "unordered" بدلاً من "hash".
تحتوي المكتبة الجديدة على أربعة أنواع من جداول التجزئة، تختلف فيما بينها بحسب قبولها للعناصر ذات المفتاح نفسه (مفاتيح فريدة أو مفاتيح متكافئة)، وما إذا كانت تربط كل مفتاح بقيمة مرتبطة به. وهي تُطابق الحاويات الترابطية الأربعة الموجودة القائمة على شجرة البحث الثنائية ، مع إضافة البادئة unordered_ .
| نوع جدول التجزئة | القيم المرتبطة | المفاتيح المتكافئة |
|---|---|---|
std::unordered_set | لا | لا |
std::unordered_multiset | لا | نعم |
std::unordered_map | نعم | لا |
std::unordered_multimap | نعم | نعم |
تُلبي الفئات الجديدة جميع متطلبات فئة الحاوية ، ولديها جميع الطرق اللازمة للوصول إلى العناصر: insert، erase، begin، end.
لم تتطلب هذه الميزة الجديدة أي إضافات إلى نواة لغة C++ (مع أن التطبيقات ستستفيد من ميزات لغة C++11 المختلفة)، بل اقتصرت على إضافة بسيطة إلى ملف الرأس <functional>وإضافة ملفات رأسية <unordered_set>أخرى <unordered_map>. لم تكن هناك حاجة إلى أي تغييرات أخرى على أي من الفئات القياسية الموجودة، وهي لا تعتمد على أي إضافات أخرى للمكتبة القياسية.
std::array و std::forward_list
بالإضافة إلى جداول التجزئة، أُضيف نوعان آخران من الحاويات إلى المكتبة القياسية. النوع الأول std::arrayعبارة عن حاوية ذات حجم ثابت، وهي أكثر كفاءة من std::vectorالمصفوفة التقليدية، وأكثر أمانًا وسهولة في الاستخدام منها. أما std::forward_listالنوع الثاني فهو قائمة مرتبطة أحادية، توفر مساحة تخزين أكثر كفاءة من القائمة المرتبطة ثنائية، std::listعندما لا تكون هناك حاجة إلى التكرار ثنائي الاتجاه.
التعبيرات النمطية
تتكون المكتبة الجديدة، المحددة في ملف الرأس الجديد <regex>، من فئتين جديدتين:
- يتم تمثيل التعبيرات النمطية بواسطة نسخة من فئة القالب
std::regex؛ - يتم تمثيل الأحداث بواسطة نسخة من فئة القالب
std::match_results، std::regex_iteratorتُستخدم للتكرار على جميع تطابقات التعبير النمطي (معstd::cregex_iteratorforconst char*وstd::sregex_iteratorforstd::string)
تُستخدم هذه الدالة std::regex_searchللبحث، بينما تُستخدم دالة أخرى للبحث والاستبدال std::regex_replaceتُعيد سلسلة نصية جديدة. [ 26 ]
إليك مثال على استخدام std::regex_iterator:
#include <regex>باستخدام std :: regex ؛ باستخدام std :: sregex_iterator ؛ باستخدام std :: string ؛string pattern = R " ( [^ ,.\t\n]+ ) " ; // البحث عن الكلمات المفصولة بمسافة، فاصلة، نقطة، علامة جدولة، سطر جديد string target = "Unseen University - Ankh-Morpork" ;regex re ( pattern ); // يُطلق استثناءً عند استخدام نمط غير صالح// استخدم regex_iterator لتحديد جميع كلمات 'target' مفصولة بأحرف 'pattern'. for ( sregex_iterator it ( target . begin (), target . end (), re ); it != re . end (); ++ it ) { string s = it -> str (); std :: cout << s << std :: endl ; }لا تتطلب المكتبة <regex>تعديل أي ملفات رأسية موجودة (مع أنها ستستخدمها عند الاقتضاء)، ولا إضافة أي جزء إلى اللغة الأساسية. في لغة C المتوافقة مع معيار POSIX ، تتوفر التعابير النمطية أيضًا عبر الملف <regex.h> .
مؤشرات ذكية متعددة الأغراض
يوفر C++11 std::unique_ptrتحسينات على std::shared_ptrTR1 std::weak_ptr. std::auto_ptrتم إيقاف استخدام TR1.
مرفق الأرقام العشوائية القابل للتوسيع
توفر مكتبة C القياسية إمكانية توليد أرقام شبه عشوائية باستخدام الدالة rand. مع ذلك، فإن خوارزمية توليد الأرقام شبه العشوائية متروكة بالكامل لمُورّد المكتبة. ورثت لغة C++ هذه الوظيفة دون أي تغييرات، لكن C++11 توفر طريقة جديدة لتوليد الأرقام شبه العشوائية.
تنقسم وظيفة توليد الأرقام العشوائية في لغة C++11 إلى جزأين: محرك توليد يحتوي على حالة مولد الأرقام العشوائية وينتج الأرقام شبه العشوائية؛ وتوزيع يحدد نطاق وتوزيع النتائج رياضياً . يتم دمج هذين الجزأين لتكوين كائن مولد الأرقام العشوائية.
بخلاف معيار لغة C rand، ستأتي آلية C++11 بثلاث خوارزميات لمحركات توليد القواعد الأساسية:
توفر لغة C++11 أيضًا عددًا من التوزيعات القياسية:
uniform_int_distribution،uniform_real_distribution،bernoulli_distribution،binomial_distribution،geometric_distribution،negative_binomial_distribution،poisson_distribution،exponential_distribution،gamma_distribution،weibull_distribution،extreme_value_distribution،normal_distribution،lognormal_distribution،chi_squared_distribution،cauchy_distribution،fisher_f_distribution،student_t_distribution،discrete_distribution،piecewise_constant_distributionوpiecewise_linear_distribution.
يتم دمج المولد والتوزيعات كما في هذا المثال:
#include <functional> #include <random>باستخدام std :: mt19937 ؛ باستخدام std :: uniform_int_distribution ؛uniform_int_distribution <int> distribution ( 0 , 99 ) ; mt19937 engine ; // Mersenne twister MT19937 auto generator = std :: bind ( distribution , engine ); int random = generator (); // توليد متغير عددي منتظم بين 0 و99. int random2 = distribution ( engine ) ; // توليد عينة أخرى مباشرةً باستخدام كائنَي التوزيع والمحرك.مرجع الغلاف
يُستَخرَج مرجع الغلاف من نسخة من قالب الفئة reference_wrapper. تُشبه مراجع الغلاف المراجع العادية (' &') في لغة C++. للحصول على مرجع غلاف من أي كائن، refيُستخدم قالب الدالة (أما للحصول على مرجع ثابت، crefفيُستَخدَم).
تُعد المراجع المُغلِّفة مفيدة بشكل خاص لقوالب الدوال، حيث تكون هناك حاجة إلى مراجع للمعاملات بدلاً من النسخ:
#include <functional> #include <iostream>// ستأخذ هذه الدالة مرجعًا إلى المعامل 'r' وتزيد قيمته. void func ( int & r ) { r ++ ; }// دالة القالب. template < class F , class P > void g ( F f , P t ) { f ( t ); }int main () { int i = 0 ; g ( func , i ); // تم إنشاء 'g<void(int& r), int>' // ثم لن يتم تعديل 'i'.std :: cout << i << std :: endl ; // الناتج -> 0g ( func , std :: ref ( i )); // يتم إنشاء 'g<void(int& r), reference_wrapper<int>>' // ثم سيتم تعديل 'i'. std :: cout << i << std :: endl ; // الناتج -> 1 }تمت إضافة هذه الأداة المساعدة الجديدة إلى <functional>الترويسة الحالية ولم تكن بحاجة إلى مزيد من التوسعات في لغة C++.
أغلفة متعددة الأشكال لكائنات الدوال
تتشابه الأغلفة متعددة الأشكال لكائنات الدوال مع مؤشرات الدوال في الدلالات والتركيب، ولكنها أقل ارتباطًا ويمكنها الإشارة بشكل عشوائي إلى أي شيء يمكن استدعاؤه (مؤشرات الدوال، أو مؤشرات الدوال الأعضاء، أو الدوال) التي تكون وسائطها متوافقة مع وسائط الغلاف.
يمكن توضيح خصائصه من خلال مثال:
#تضمين <وظيفي>باستخدام std :: function ؛ باستخدام std :: plus ؛// إنشاء غلاف باستخدام فئة القالب 'function'. function < int ( int , int ) > func ;plus < int > add ; // تم تعريف 'plus' على أنه 'template <class T> T plus(T, T) ;' // ثم يكون نوع 'add' هو 'int add( int x, int y )'.func = add ; // OK - المعاملات وأنواع الإرجاع هي نفسها.int a = func ( 1 , 2 ); // ملاحظة: إذا لم يُشر المُغلِّف 'func' إلى أي دالة، // فسيتم طرح الاستثناء 'std::bad_function_call'.function < bool ( short , short ) > func2 ; if ( ! func2 ) { // صحيح لأن 'func2' لم يتم تعيين دالة لها بعد.bool neighbour ( long x , long y ); func2 = & neighbour ; // OK - المعاملات وأنواع الإرجاع قابلة للتحويل.struct Test { bool operator ()( short x , short y ); };سيارة اختبار ؛ // 'std::ref' هي دالة قالب تُعيد غلاف // الدالة العضوية 'operator()' من بنية 'car'. func = std :: ref ( car ); } func = func2 ; // صحيح - المعاملات وأنواع الإرجاع قابلة للتحويل.تم تعريف فئة القالب functionداخل ملف الرأس <functional>، دون الحاجة إلى أي تغيير في لغة C++.
سمات الكتابة للبرمجة الوصفية
تتألف البرمجة الوصفية من إنشاء برنامج يقوم بإنشاء أو تعديل برنامج آخر (أو تعديل نفسه). يمكن أن يحدث هذا أثناء الترجمة أو أثناء التنفيذ. وقد قررت لجنة معايير لغة C++ تقديم مكتبة للبرمجة الوصفية أثناء الترجمة باستخدام القوالب.
فيما يلي مثال على برنامج ميتا باستخدام معيار C++03: تكرار لنسخ القوالب لحساب الأسس الصحيحة:
template < int B , int N > struct Power { // استدعاء متكرر وإعادة تركيب. enum { VALUE = B * Power < B , N - 1 >:: VALUE }; };template < int B > struct Power < B , 0 > { // ''N == 0'' شرط الإنهاء. enum { VALUE = 1 }; };int quartic_of_three = Power < 3 , 4 >:: VALUE ;تستطيع العديد من الخوارزميات العمل على أنواع مختلفة من البيانات؛ وتدعم قوالب لغة C++ البرمجة العامة ، مما يجعل الشيفرة البرمجية أكثر اختصارًا وفائدة. ومع ذلك، من الشائع أن تحتاج الخوارزميات إلى معلومات حول أنواع البيانات المستخدمة. ويمكن استخراج هذه المعلومات أثناء إنشاء نسخة من فئة القالب باستخدام سمات النوع .
تُحدد سمات النوع فئة الكائن وجميع خصائص الفئة (أو البنية). ويتم تعريفها في ملف الرأس الجديد <type_traits>.
في المثال التالي توجد دالة القالب 'elaborate' التي، اعتمادًا على أنواع البيانات المعطاة، ستقوم بإنشاء أحد الخوارزميتين المقترحتين ( Algorithm::perform).
#include <type_traits>باستخدام std :: is_floating_point ؛ باستخدام std :: is_integral ؛// الطريقة الأولى للتشغيل. template < bool B > struct Algorithm { template < class T1 , class T2 > static int perform ( T1 & , T2 & ) { // ... } };// الطريقة الثانية للتشغيل. template <> struct Algorithm < true > { template < class T1 , class T2 > static int perform ( T1 , T2 ) { // ... } };// سيؤدي إنشاء مثيل للدالة 'elaborate' إلى إنشاء مثيل تلقائيًا بالطريقة الصحيحة للعمل. template < class T1 , class T2 > int factorsup ( T1 A , T2 B ) { // استخدم الطريقة الثانية فقط إذا كان 'T1' عددًا صحيحًا وإذا كان 'T2' // عددًا عشريًا، وإلا فاستخدم الطريقة الأولى. return Algorithm < is_integral <T1> :: value && is_floating_point <T2> :: value > :: perform ( A , B ) ; }من خلال سمات النوع ، المحددة في رأس الملف <type_traits>، من الممكن أيضًا إنشاء عمليات تحويل النوع ( static_castوهي const_castغير كافية داخل القالب).
ينتج هذا النوع من البرمجة شفرة أنيقة وموجزة؛ ومع ذلك، فإن نقطة الضعف في هذه التقنيات هي تصحيح الأخطاء: فهي غير مريحة أثناء التجميع وصعبة للغاية أثناء تنفيذ البرنامج.
طريقة موحدة لحساب نوع القيمة المرجعة لكائنات الدوال
تحديد نوع القيمة المُعادة لكائن دالة قالبية أثناء وقت الترجمة ليس بالأمر البديهي، خاصةً إذا كانت القيمة المُعادة تعتمد على مُعاملات الدالة. على سبيل المثال:
struct Clear { // نوع المعامل يساوي نوع القيمة المرجعة. int operator ()( int ) const ; double operator ()( double ) const ; };template < class Obj > class Calculus { private : Obj member ; public : template < class Arg > Arg operator ()( Arg & a ) const { return member ( a ); } };عند إنشاء نسخة من قالب الفئة ، سيكون لكائن الدالة دائمًا نفس نوع الإرجاع لكائن الدالة . ومع ذلك، بالنظر إلى الفئة أدناه:Calculus<Clear>calculusClearConfused
struct Confused { // نوع المعامل لا يساوي نوع القيمة المرجعة. double operator ()( int ) const ; int operator ()( double ) const ; };محاولة إنشاء نسخة من الكائن ستؤدي إلى اختلاف نوع القيمة المُعادة من الكائن عن نوع القيمة المُعادة من الفئة . وقد يُصدر المُصرّف تحذيرات بشأن التحويل من النوع الأول إلى النوع الثاني والعكس.Calculus<Confused>CalculusConfusedintdouble
يُقدّم TR1، ويتبنّى C++11، فئة القالب std::result_ofالتي تسمح بتحديد واستخدام نوع القيمة المُعادة لكائن الدالة لكل تعريف. CalculusVer2يستخدم الكائن std::result_ofالكائن لاستنتاج نوع القيمة المُعادة لكائن الدالة.
#include <type_traits>باستخدام std :: result_of ;template < class Obj > class CalculusVer2 { private : Obj member ; public : template < class Arg > typename result_of < Obj ( Arg ) >:: type operator ()( Arg & a ) const { return member ( a ); } };وبهذه الطريقة، في حالات كائن الدالة، لا توجد تحويلات أو تحذيرات أو أخطاء.CalculusVer2<Confused>
التغيير الوحيد عن إصدار TR1 هو std::result_ofأن إصدار TR1 كان يسمح للتطبيقات بالفشل في تحديد نوع نتيجة استدعاء الدالة. وبسبب التغييرات التي طرأت على لغة C++ لدعم هذه الميزة decltype، لم يعد إصدار C++11 std::result_ofبحاجة إلى هذه الحالات الخاصة؛ إذ يُشترط على التطبيقات حساب النوع في جميع الحالات.
تحسين توافق لغة C
لتحقيق التوافق مع لغة C ، تمت إضافة ما يلي بدءًا من C99: [ 27 ]
- المعالج المسبق: [ 28 ]
- وحدات الماكرو المتغيرة ،
- دمج سلاسل نصية متجاورة ضيقة/واسعة،
_Pragma()– ما يعادل#pragma.
long long– نوع عدد صحيح لا يقل طوله عن 64 بت.__func__– تقييم الماكرو إلى اسم الدالة التي يوجد بها.- العناوين:
cstdbool(stdbool.h),cstdint(stdint.h),cinttypes(inttypes.h).
ميزات كان من المخطط لها في الأصل ولكن تم حذفها أو عدم تضمينها
متجه إلى تقرير رحلة منفصل:
مؤجل:
- المفاهيم
- دعم أكثر اكتمالاً أو مطلوباً لجمع القمامة
- انعكاس
- نطاقات الماكرو
الميزات التي تمت إزالتها أو إيقافها
تم حذف مصطلح نقطة التسلسل ، واستُبدل بتحديد إما أن عملية واحدة تُنفذ قبل أخرى، أو أن عمليتين غير متسلسلتين. [ 30 ]
تم حذف استخدام الكلمة المفتاحية export، بالإضافة إلى القوالب المصدرة . [ 31 ] ظلت الكلمة exportالمفتاحية محجوزة، وأعيد تقديمها لاحقًا في C++20 مع إضافة الوحدات النمطية .
تم إيقاف استخدام مواصفات الاستثناءات الديناميكية . [ 31 ] تتوفر مواصفات وقت الترجمة للوظائف التي لا تُطلق استثناءات باستخدام الكلمة noexceptالمفتاحية، وهو أمر مفيد للتحسين.
std::auto_ptrتم إيقاف استخدامها، بعد أن تم استبدالها بـ std::unique_ptr.
تم إيقاف استخدام فئات الكائنات الأساسية للوظائف ( std::unary_function, std::binary_function)، والمحولات إلى مؤشرات للوظائف والمحولات إلى مؤشرات للأعضاء، وفئات الربط.
ينص الملحق د.2 على ما يلي: "إن استخدام الكلمة registerالرئيسية كمحدد لفئة التخزين (§7.1.1) أصبح مهملاً."
انظر أيضاً
مراجع
- ↑ «لدينا معيار دولي: C++0x معتمد بالإجماع» . ١٢ أغسطس ٢٠١١. مؤرشف من الأصل في ١١ ديسمبر ٢٠١٨. تم الاطلاع عليه في ١٢ أغسطس ٢٠١١ .
- 1 2 ستروستروب، بيارن. "أسئلة وأجوبة حول C++11" . stroustrup.com . مؤرشف من الأصل بتاريخ 2018-10-06 . تم الاطلاع عليه بتاريخ 2014-10-15 .
- ١ ٢ "نظرة عامة على C++11: ما هي أهداف التصميم المحددة التي استرشدت بها اللجنة؟" . معيار C++ . مؤرشف من الأصل بتاريخ ٣١ يناير ٢٠١٩. تم الاطلاع عليه بتاريخ ٤ سبتمبر ٢٠١٥ .
- ↑ "بيارن ستروستروب: نظرة عامة على C++0x" (ملف PDF) . مؤرشف (ملف PDF) من الأصل بتاريخ 17 يونيو 2016. تم الاطلاع عليه بتاريخ 30 يونيو 2011 .
- ↑ "ISO/IEC 14882:2011" . المنظمة الدولية للمقاييس. 2 سبتمبر 2011. مؤرشف من الأصل في 29 يناير 2013. تم الاطلاع عليه في 3 سبتمبر 2011 .
- ↑ "مسودة عمل، معيار للغة البرمجة C++" (ملف PDF) . مؤرشف (ملف PDF) من الأصل بتاريخ 21 يناير 2019. تم الاطلاع عليه بتاريخ 26 أبريل 2012 .
- ↑ "المعيار" . مؤسسة معيار C++ . مؤرشف من الأصل بتاريخ 5 نوفمبر 2012. تم الاطلاع عليه بتاريخ 2 نوفمبر 2012.
باستثناء المعايير/التقارير النهائية فقط، فإن جميع وثائق لجنة C++ متاحة للجمهور مجانًا، بما في ذلك جميع مسودات العمل، والتي يقترب العديد منها من المعيار المنشور. تحتوي مسودة العمل لشهر يناير 2012 على معيار C++11 بالإضافة إلى تعديلات تحريرية طفيفة.
- ↑ "Clang - حالة لغة برمجة C++" . 29-11-2023. مؤرشف من الأصل في 29-11-2023 . تم الاطلاع عليه في 1-12-2023 .
- ↑ "إصدار GCC 4.8.1، اكتمال ميزات C++11: معيار C++" . isocpp.org . تم الاطلاع عليه بتاريخ 1 ديسمبر 2023 .
- ↑ ألكسندر سوتر، "معايير ترميز C++" رقم 15
- ↑ غابرييل دوس ريس؛ بيارن ستروستروب (22 مارس 2010). "تعبيرات الثوابت العامة للغات برمجة الأنظمة، وقائع مؤتمر SAC '10" (ملف PDF) . مؤرشف (ملف PDF) من الأصل في 13 يونيو 2018. تم الاطلاع عليه في 18 أغسطس 2012 .
- ↑ جاكو يارفي؛ بيارن ستروستروب؛ دوغلاس غريغور؛ جيريمي سيك (28 أبريل 2003). "Decltype و auto، لغة البرمجة C++، رقم الوثيقة: N1478=03-0061" (ملف PDF) . مؤرشف (PDF) من الأصل في 28 مايو 2015. تم الاطلاع عليه في 6 يونيو 2015 .
- ↑ روجر أور (يونيو 2013). ""السيارات - شر لا بد منه؟" مجلة أوفرلود، العدد 115. مؤرشف من الأصل بتاريخ 6 يونيو 2015. تم الاطلاع عليه بتاريخ 6 يونيو 2015 .
- ↑ "رقم الوثيقة: N1968=06-0038 - تعابير لامدا والإغلاقات للغة C++" (ملف PDF) . المعايير المفتوحة. مؤرشف (PDF) من الأصل بتاريخ 28 يوليو 2011. تم الاطلاع عليه بتاريخ 20 أبريل 2009 .
- ↑ "Decltype (المراجعة 5)" (ملف PDF) . مؤرشف (ملف PDF) من الأصل بتاريخ 14 فبراير 2022. تم الاطلاع عليه بتاريخ 16 فبراير 2022 .
- ↑ "المحدد التلقائي (منذ C++11) - cppreference.com" . en.cppreference.com . مؤرشف من الأصل بتاريخ 20-10-2016 . تم الاطلاع عليه بتاريخ 18-10-2016 .
- ↑ جوستيدت، ينس (9 يوليو 2019). "تقديم ثابت nullptr - الإصدار 1" (ملف PDF) . سجل وثائق اللجنة الفنية المشتركة 1/اللجنة الفرعية 22/مجموعة العمل 14 التابعة للمنظمة الدولية للتوحيد القياسي. مؤرشف (ملف PDF) من الأصل بتاريخ 27 يوليو 2020. تم الاطلاع عليه بتاريخ 19 أبريل 2020 عبر موقع open-std.org.
- ↑ تسبب هذا في تعارض مع الاستخدام المقترح (الشائع في لغات أخرى) لعلامة الشرطة السفلية لتجميع الأرقام في القيم العددية مثل الأعداد الصحيحة ، لذا يستخدم C++14 بدلاً من ذلك الفاصلة العليا ( كفاصلة علوية ) للتجميع. ديفيد فانديفورد (21-09-2012). "N3448: فصل الأرقام بسلاسة" (ملف PDF) . مؤرشف (PDF) من الأصل في 11-08-2015 . تم الاطلاع عليه في 13-08-2015 .لورانس كرول (19 ديسمبر 2012). "N3499: فواصل الأرقام" . مؤرشف من الأصل بتاريخ 11 أغسطس 2015. تم الاطلاع عليه بتاريخ 13 أغسطس 2015 .
- ↑ ISO / IEC (2003). ISO/IEC 14882 :2003(E): لغات البرمجة – C++ §3.2 قاعدة التعريف الواحد [basic.def.odr] الفقرة 3
- 1 2 "الوظائف الافتراضية والمحذوفة - ISO/IEC JTC1 SC22 WG21 N2210 = 07-0070 - 2007-03-11" . مؤرشف من الأصل بتاريخ 2012-08-19 . تم الاطلاع عليه بتاريخ 2012-12-20 .
- ↑ "استخدام مجموعة مترجمات جنو (GCC): Long Long" . gcc.gnu.org . مؤرشف من الأصل بتاريخ 21 أغسطس 2016. تم الاطلاع عليه بتاريخ 25 يوليو 2016 .
- ↑ "نطاقات أنواع البيانات (C++)" . مؤرشف من الأصل بتاريخ 21-02-2009 . تم الاطلاع عليه بتاريخ 23-04-2009 .
- ↑ صموئيل ب. هاربيسون الثالث، جاي ل. ستيل الابن: "ج - دليل مرجعي"، الطبعة الخامسة، ص 251
- ↑ جيه إف باستيان؛ أليسدير ميريديث (2021-04-16). "إزالة دعم جمع البيانات المهملة" .
- ↑ ميليفسكي، بارتوش (3 مارس 2009). "وعود منقوضة - مستقبلات C++0x" . مؤرشف من الأصل في 16 سبتمبر 2011. تم الاطلاع عليه في 24 يناير 2010 .
- ↑ "مكتبة التعبيرات النمطية للغة C++" . cppreference.com . تم الاطلاع عليه بتاريخ 10 ديسمبر 2022 .
- ↑ "حالة Clang - C++98 وC++11 وC++14" . Clang.llvm.org. ١٢ مايو ٢٠١٣. مؤرشف من الأصل في ٢٨ مايو ٢٠١٩. تم الاطلاع عليه في ١٠ يونيو ٢٠١٣ .
- ↑ "مسودة تغييرات العمل لمزامنة المعالج المسبق C99" . www.open-std.org . مؤرشف من الأصل بتاريخ 31 يوليو 2020. تم الاطلاع عليه بتاريخ 26 مايو 2014 .
- ^ ديفيد فانديفورد (11 يناير 2011). "الوحدات النمطية في C++" (PDF) . open-std.org . WG21.
- ↑ كيفز، جوناثان (4 يونيو 2007). "تحديث حول معيار لغة C++-0x" . مؤرشف من الأصل في 9 سبتمبر 2011. تم الاطلاع عليه في 25 مايو 2010 .
- 1 2 ساتر، هيرب (3 مارس 2010). "تقرير رحلة: اجتماع معايير ISO C++ لشهر مارس 2010" . مؤرشف من الأصل في 11 يوليو 2018. تم الاطلاع عليه في 24 مارس 2010 .
روابط خارجية
- لجنة معايير لغة C++
- C++0X: الوجه الجديد للغة C++ القياسية
- تغطية مدونة هيرب ساتر لـ C++11
- تغطية مدونة أنتوني ويليامز لـ C++11
- محاضرة حول لغة C++0x ألقاها بيارن ستروستروب في جامعة واترلو، مؤرشفة بتاريخ 23 يناير 2009 على موقع Wayback Machine.
- حالة اللغة: مقابلة مع بيارن ستروستروب (15 أغسطس 2008)، مؤرشفة في 31 يناير 2009 على موقع Wayback Machine
- صفحة ويكي للمساعدة في تتبع ميزات لغة C++ 0x الأساسية ومدى توفرها في المترجمات
- مرجع مكتبة C++11 القياسية عبر الإنترنت
- مترجم C++11 عبر الإنترنت
- الأسئلة الشائعة حول لغة C++11 من بيارن ستروستروب
- مزيد من المعلومات حول ميزات C++11: حلقة for المستندة إلى النطاق، وسبب إهمال auto_ptr، إلخ.
- لغة سي++
- معايير لغات البرمجة
- عائلة لغات البرمجة C++
- معايير اللجنة الكهروتقنية الدولية
- معايير ISO
