c++ - لماذا أحتاج إلى استخدام typenef typename في g++ ولكن ليس VS؟




typedef (4)

لقد مضى وقت طويل منذ أن أمسك بي مجلس التعاون الخليجي بهذا ، لكنه حدث اليوم. ولكني لم أفهم أبدا لماذا يتطلب GCC اسم typedef ضمن قوالب ، بينما VS وأنا أعتقد أن ICC لا. هل ما هو رمز typedef typename هو "bug" أو a standard standard ، أو شيء ما متروك لكتّاب المترجمين؟

بالنسبة لأولئك الذين لا يعرفون ما أعنيه هنا هي عينة:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    std::map<KEY,VALUE>::const_iterator iter = container.find(key);
    return iter!=container.end();
}

التعليمات البرمجية المذكورة أعلاه في VS (وربما في ICC) ، لكنها فشلت في دول مجلس التعاون الخليجي لأنها تريد ذلك على النحو التالي:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    typedef typename std::map<KEY,VALUE>::const_iterator iterator; //typedef typename
    iterator iter = container.find(key);
    return iter!=container.end();
}

ملاحظة: هذه ليست وظيفة فعلية أستخدمها ، ولكن مجرد شيء سخيف يوضح المشكلة.


تجدر الإشارة إلى أن مشكلة تحديد القيمة / النوع ليست هي المشكلة الأساسية. المشكلة الأساسية هي التحليل . يعتبر

template<class T>
void f() { (T::x)(1); }

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

(a)(b)(c)

في حال لم تتذكر ، فإن الأسبقية لها أولوية أعلى من دالة المكالمة في C ، أحد الأسباب التي دفعت Bjarne إلى البحث عن نمط الوظيفة. ولذلك لا يمكن معرفة ما إذا كان ما سبق يعني

(a)(b)  (c)   // a is a typename

أو

(a) (b)(c)    // a is not a typename , b is

أو

(a)(b) (c)    // neither a nor b is a typename

حيث أدخلت مساحة للإشارة إلى التجميع.

لاحظ أيضًا أن الكلمة الرئيسية "templatename" مطلوبة لنفس السبب مثل "typename" ، لا يمكنك تحليل الأشياء دون معرفة نوعها في C / C ++.


حسنا ، دول مجلس التعاون الخليجي لا تتطلب في الواقع typedef - typename يكفي. هذا يعمل:

#include <iostream>
#include <map>

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    typename std::map<KEY,VALUE>::const_iterator iter = container.find(key);
    return iter!=container.end();
}

int main() {
    std::map<int, int> m;
    m[5] = 10;
    std::cout << find(m, 5) << std::endl;
    std::cout << find(m, 6) << std::endl;
    return 0;
}

هذا مثال لمشكلة تحليل حساسة للسياق. ما يعني أن السطر المعني غير ظاهر من بناء الجملة في هذه الوظيفة فقط - تحتاج إلى معرفة ما إذا كان std::map<KEY,VALUE>::const_iterator هو نوع أم لا.

الآن ، لا يبدو لي أن أفكر في مثال على ما ... قد يكون ::const_iterator باستثناء نوع ، من شأنه أيضا أن لا يكون خطأ. لذا أعتقد أن المترجم يمكنه أن يكتشف أنه يجب أن يكون نوعًا ، ولكن قد يكون من الصعب على المترجم الضعيف (الكتاب).

يتطلب المعيار استخدام اسم typename هنا ، وفقًا لـ litb حسب القسم 14.6 / 3 من المعيار.


هذا خطأ في مترجم Microsoft C ++ - في المثال الخاص بك ، قد لا يكون std :: map :: iterator نوعًا (يمكن أن يكون لديك std :: map متخصص على KEY ، VALUE بحيث std :: map :: iterator كان متغير على سبيل المثال).

تقوم قوات دول مجلس التعاون الخليجي بكتابة الكود الصحيح (حتى لو كان ما قصدته واضحًا) ، في حين أن مترجم Microsoft يخمن بشكل صحيح ما تعنيه (على الرغم من أن الشفرة التي كتبتها غير صحيحة).


يبدو أن VS / ICC تزود الكلمة الرئيسية typename حيثما تعتقد أنها مطلوبة. لاحظ أن هذا أمر سيئ (TM) - للسماح لل مترجم أن يقرر ما تريد. هذا يزيد من تعقيد القضية عن طريق غرس هذه العادة السيئة لتخطي اسم typename عند الحاجة وهو كابوس قابلية. هذا بالتأكيد ليس السلوك القياسي. حاول في الوضع القياسي الصارم أو Comeau.





typename