typedef مقابل using في C++: دليل كامل للمطور الحديث
\n\nهل تساءلت يومًا لماذا يكتب بعض المطورين typedef في كود C++ بينما يكتب آخرون using؟ يبدو أن كلاهما يفعل الشيء نفسه - إنشاء أسماء مستعارة للأنواع. ولكن هل هذا صحيح حقًا؟ إذا كنت تريد كتابة كود نظيف وحديث وتجنب الوقوع في فخاخ الإنشاءات القديمة، فهذه المقالة لك.
سنقوم بتحليل الاختلافات بين typedef و using في C++، ومتى نستخدم أيهما، ولماذا يميل مجتمع C++ بشكل متزايد نحو using. هل أنت مستعد؟ لنبدأ!
تاريخ موجز: من C إلى C++11
\n\ntypedef جاء إلينا من لغة C. إنها الطريقة التقليدية للإعلان عن اسم مستعار لنوع. تبدو كالتالي:
\n\ntypedef unsigned long long ULL;\ntypedef std::vector<std::string> StringVector;\ntypedef void (*FunctionPtr)(int, double);\n\nبناء الجملة، بعبارة ملطفة، ليس الأكثر بديهية. هذا ملحوظ بشكل خاص مع المؤشرات للدوال - حتى المطورين ذوي الخبرة يتلعثمون أحيانًا عند قراءة مثل هذه التصريحات.
\n\nفي C++11، ظهر بناء جديد - using (أو alias declaration). يبدو أكثر طبيعية بكثير:
\n\nusing ULL = unsigned long long;\nusing StringVector = std::vector<std::string>;\nusing FunctionPtr = void (*)(int, double);\n\nيتضح على الفور: على اليسار - الاسم، على اليمين - النوع. يُقرأ كـ "إسناد"، وهو أمر بديهي لأي مبرمج.
\n\nالفرق الرئيسي: العمل مع القوالب
\n\nهنا يكمن الاختلاف الأكثر أهمية بين typedef و using. using يدعم alias templates (الأسماء المستعارة القالبية)، بينما typedef لا يدعمها.
دعنا ننظر إلى مثال. لنفترض أنك تريد إنشاء اسم مستعار لـ std::unique_ptr مع محوّل مخصص:
// باستخدام using - سهل وجميل\ntemplate <typename T>\nusing CustomPtr = std::unique_ptr<T, void(*)(T*)>;\n\n// باستخدام typedef - غير ممكن مباشرة!\n// سيتعين عليك بناء أغلفة عبر الهياكل\ntemplate <typename T>\nstruct CustomPtrOld {\n typedef std::unique_ptr<T, void(*)(T*)> type;\n};\n\n// الاستخدام:\nCustomPtr<int> ptr1; // using - بسيط\nCustomPtrOld<int>::type ptr2; // typedef - معقد\n\nهل تشعر بالفرق؟ using يجعل الكود أنظف وأقصر. وإذا كنت بحاجة لاستخدام هذا الاسم المستعار داخل قالب فئة أو دالة، يصبح الفرق أكثر وضوحًا:
template <typename T>\nclass Container {\npublic:\n // باستخدام using\n using Ptr = CustomPtr<T>;\n \n // باستخدام typedef - تحتاج typename و ::type\n typedef typename CustomPtrOld<T>::type OldPtr;\n \nprivate:\n Ptr data;\n OldPtr oldData;\n};\n\nقابلية قراءة الكود: الفائز الواضح
\n\nحتى بالنسبة للأنواع البسيطة، يفوز using في قابلية القراءة. قارن:
// باستخدام typedef - غير واضح أين الاسم وأين النوع\ntypedef std::map<std::string, std::vector<int>> StringToIntVectorMap;\n\n// باستخدام using - كل شيء واضح\nusing StringToIntVectorMap = std::map<std::string, std::vector<int>>;\n\nعند قراءة الكود، تقوم عينك بشكل طبيعي بمسح البناء من اليسار إلى اليمين. using يتبع هذا المبدأ: أولاً الاسم، ثم النوع. بينما typedef يجبرك على قراءة النوع أولاً ثم الاسم - وهذا يتعارض مع الحدس.
المؤشرات للدوال: ألم typedef
\n\nالمؤشرات للدوال هي ربما الجزء الأقل تفضيلاً في C++ للعديد من المطورين. و typedef يجعلها أكثر إرباكًا:
// typedef - اقرأها كتعويذة\ntypedef void (*Callback)(int, const std::string&);\n\n// using - كمتغير عادي\nusing Callback = void (*)(int, const std