دع المترجم يختار النوع
في الصفحة السابقة رأيت أن لكل متغير في C++ نوعًا ثابتًا - int وdouble وstd::string وهكذا. كتابة هذا النوع باليد لا بأس بها في int count = 0;، لكنها تصبح مزعجة بمجرد أن تطول الأنواع. تتيح الكلمة المفتاحية auto (في C++11 وما بعده) للمترجم أن يستنتج النوع من القيمة التي تُسندها، فتكتب القيمة مرة واحدة وتدع المترجم يملأ الباقي.
الفكرة الأساسية: auto ليست كتابة ديناميكية للأنواع. لكل متغير ما زال نوع واحد ملموس، مثبَّت وقت الترجمة. auto تُعفيك فقط من كتابته.
لماذا تستحق auto مكانتها
بالنسبة إلى الأنواع القصيرة الواضحة، فإن auto count = 0; وint count = 0; متساويتان في سهولة القراءة. حيث تُؤتي auto ثمارها حقًا هو مع أسماء الأنواع الطويلة المتكررة - النوع الذي تحصل عليه من حاويات المكتبة القياسية ومُكرِّراتها.
قارن النسخة المطوّلة بنسخة auto:
// بدون auto - النوع مكتوب مرتين فعليًا
std::vector<std::pair<std::string, int>>::iterator it = scores.begin();
// مع auto - المترجم يعرف النوع مسبقًا
auto it = scores.begin();
كلتاهما تُصرّحان عن نوع المُكرِّر نفسه تمامًا. الثانية أسهل في القراءة ولن تخرج عن التزامن إذا غيّرت لاحقًا scores إلى حاوية مختلفة.
وها هي داخل برنامج كامل:
auto في حلقات for المعتمدة على النطاق
أكثر مكان ستلتقي فيه بـauto هو حلقة for المعتمدة على النطاق. نادرًا ما تريد كتابة نوع العنصر باليد، وطريقة كتابتك لـauto تحدّد ما إذا كنت ستحصل على نسخة أم مرجع.
ثلاثة أشكال ستراها، وما تعنيه كل منها:
for (auto x : v)-xهي نسخة من كل عنصر. رخيصة لـint، ومُبذِّرة للكائنات الكبيرة.for (auto& x : v)-xهي مرجع؛ يمكنك تعديل العناصر في مكانها.for (const auto& x : v)-xمرجع للقراءة فقط. استخدمها عندما تحتاج إلى القراءة فحسب.
البرنامج التالي يُعدّل الحاوية عبر auto&:
مطبّ: اكتب for (auto n : nums) (دون &) في تلك الحلقة وسيُغيّر n *= 10 النسخة وحدها بصمت، تاركًا nums دون مساس. لن يُحذّرك المترجم - فالحلقة ببساطة لا تفعل أي شيء مفيد.
ما الذي تُسقطه auto
الـauto العادية تستنتج النوع بالطريقة نفسها التي يفعلها مُعامل دالة يُمرَّر بالقيمة: فهي تُسقط const ذات المستوى الأعلى والمراجع وvolatile. هذا يعني أن auto تُعطيك دائمًا نسخة جديدة قابلة للتعديل ما لم تطلب خلاف ذلك.
إذا أردت الاحتفاظ بـconst أو تجنّب النسخ، تُضيف المُحدِّدات بنفسك. النمط هو أن تُزيّن auto تمامًا كما تُزيّن أي نوع:
إذن auto تستنتج النوع الأساس؛ و& وconst و* مقابض تُضيفها فوقه. auto هي النوع، وconst auto& مرجع للقراءة فقط إليه.
الأخطاء والمطبّات الشائعة
auto تُزيل عناء الكتابة، لا الحاجة إلى فهم الأنواع. ثمة بضع فخاخ توقع المبتدئين:
يجب أن تُهيّئ. ليس لدى auto ما تستنتج منه في تصريح فارغ، لذا فهذا خطأ ترجمة قاطع:
auto x; // error: declaration of 'auto x' has no initializer
auto y = 0; // لا بأس
القيم الحرفية الصحيحة من النوع int، لا double. auto half = 1 / 2; تستنتج int وتخزّن 0، لأن 1 / 2 قسمة صحيحة قبل أن تراها auto أصلًا. النوع يتبع القيمة:
auto تُسقط المرجع - احذر مفاجآت النسخة المعلّقة. إذا أعادت دالة مرجعًا والتقطته بـauto عادية، فستحصل على نسخة، وهذا أحيانًا عيب أداء حقيقي في حلقة كثيفة التنفيذ (نسخ عميق لكائن كبير في كل تكرار). الجأ إلى const auto& عندما تقصد «انظر ولا تأخذ».
لا تُخفِ النوع حين يكون مهمًّا. auto result = compute(); لا بأس بها حين يكون نوع إرجاع compute واضحًا من السياق، لكن إذا اضطر القارئ إلى البحث عمّا تكونه result فعلًا، فقد تكون كتابة النوع صراحةً الخيار الألطف. auto لتقليل الضجيج، لا لإخفاء القصد.
التالي: الثوابت و const
رأيت الآن أن auto تُسقط const عمدًا ما لم تطلب الاحتفاظ به - وهذا يطرح السؤال البديهي: ما الذي يضمنه const فعلًا، ومتى ينبغي أصلًا أن تُعلِّم قيمةً بأنها غير قابلة للتغيير؟ الصفحة التالية تتعمّق في const والتعبيرات الثابتة، ولماذا يُعدّ «اجعله const افتراضيًا» من أنفع العادات في C++.
الأسئلة الشائعة
ماذا تفعل الكلمة المفتاحية auto في C++؟
auto تُخبر المترجم باستنتاج نوع المتغير من مُهيّئه. auto x = 5; تجعل x من النوع int؛ وauto y = 3.14; تجعل y من النوع double. يُثبَّت النوع وقت الترجمة - فـauto ليست كتابة ديناميكية للأنواع، بل اختصار يغنيك عن كتابة النوع بنفسك.
هل تحتفظ auto بـ const والمراجع في C++؟
لا. الـauto العادية تُسقط const ذات المستوى الأعلى والمراجع وvolatile. إذا كان المصدر const int& r، فإن auto x = r; تُنتج نسخة int عادية. للاحتفاظ بها تكتبها صراحةً: استخدم const auto& لربط مرجع للقراءة فقط دون نسخ.
هل يمكن التصريح عن متغير بـ auto دون تهيئته؟
لا. auto x; خطأ ترجمة لأنه لا يوجد مُهيّئ يستنتج منه المترجم النوع. يجب إعطاء كل متغير auto قيمةً عند نقطة التصريح عنه.