c++ - شرح - لماذا لا يتم تجميع هذا دون مُنشئ افتراضي؟




default constructor java (2)

هنا هو تحذير رنة

truct_init.cpp: 11: 11: خطأ: إعادة تعريف 'num' بنوع مختلف: 'Boo' vs 'int'

أستطيع أن أفعل ذلك:

#include <iostream>

int counter;

int main()
{
    struct Boo
    {
        Boo(int num)
        {
            ++counter;
            if (rand() % num < 7) Boo(8);
        }
    };

    Boo(8);

    return 0;
}

هذا سوف يجمع بشكل جيد ، النتيجة المضادّة هي 21 . ومع ذلك ، عندما أحاول إنشاء كائن Boo بتمرير وسيطة المنشئ بدلاً من حرفي صحيح ، تظهر لي خطأ في التحويل البرمجي:

#include <iostream>

int counter;

int main()
{
    struct Boo
    {
        Boo(int num)
        {
            ++counter;
            if (rand() % num < 7) Boo(num); // No default constructor 
                                            // exists for Boo
        }
    };

    Boo(8);

    return 0;
}

كيف يتم استدعاء مُنشئ افتراضي في المثال الثاني ولكن ليس في المثال الأول؟ هذا هو الخطأ الذي أحصل عليه في Visual Studio 2017.

على المحول البرمجي على الإنترنت C ++ onlineGDB أحصل على الأخطاء:

error: no matching function for call to main()::Boo::Boo()’
    if (rand() % num < 7) Boo(num);

                           ^
note:   candidate expects 1 argument, 0 provided

يقدم Clang رسالة التحذير هذه:

<source>:12:16: warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'num' [-Wvexing-parse]
            Boo(num); // No default constructor 
               ^~~~~

هذه مشكلة تحليل محيرة للغاية. لأن Boo هو اسم نوع الفئة و num ليس اسم كتابة ، Boo(num); يمكن أن يكون إما بناء مؤقت من النوع Boo مع وجود num للوسيطة إلى مُنشئ Boo أو يمكن أن يكون عبارة عن إعلان Boo num; مع أقواس إضافية حول رقم num (الذي قد يكون num دائمًا). إذا كان كلاهما تفسيرات صحيحة ، يتطلب المعيار من المحول البرمجي افتراض إعلان.

إذا تم تحليله كإعلان ، فعندئذ Boo num; قد يطلق عليه المُنشئ الافتراضي (المُنشئ بدون وسيطات) ، والذي لم يتم الإعلان عنه بواسطتك أو ضمنيًا (لأنك أعلنت مُنشئًا آخر). لذلك البرنامج غير صحيح.

هذه ليست مشكلة مع Boo(8); ، نظرًا لأن 8 لا يمكن أن تكون معرفًا للمتغير (معرِّف المُعرِّف) ، لذلك يتم تحليله على أنه استدعاء لإنشاء Boo مؤقت مع 8 كوسيطة إلى المُنشئ ، وبالتالي عدم استدعاء المُنشئ الافتراضي (الذي لم يتم الإعلان عنه) ، ولكن المعرفة يدويا.

يمكنك إزالة الغموض عن هذا من إعلان إما باستخدام Boo{num}; بدلا من Boo(num); (لأن {} حول المُعلن غير مسموح به) ، بجعل المتغير المؤقت مسمى ، على سبيل المثال Boo temp(num); أو عن طريق وضعه كمعامل في تعبير آخر ، على سبيل المثال (Boo(num)); ، (void)Boo(num); الخ

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

على أي حال ، لا يبدو من المستحسن إساءة استخدام إنشاء كائن مؤقت لشيء يجب أن يكون استدعاء دالة (عضو) عادي.

لا يمكن أن يحدث هذا النوع المعين من التحليل الأكثر حرجًا والذي يحمل اسمًا واحدًا غير كتابة في الأقواس إلا لأن القصد من ذلك هو إنشاء مؤقت وتجاهله على الفور أو بدلاً من ذلك إذا كان القصد هو إنشاء مؤقت يستخدم مباشرةً كمُهيئ ، على سبيل المثال Boo boo(Boo(num)); (في الواقع تعلن الدالة boo أخذ معلمة باسم num بنوع Boo وإرجاع Boo ).

عادةً ما لا يكون التخلص من الصدور فوريًا مقصودًا ويمكن تجنب حالة أداة التهيئة باستخدام تهيئة الدعامة أو المظاهر المزدوجة ( Boo boo{Boo(num)} أو Boo boo(Boo{num}) أو Boo boo((Boo(num))); ، ولكن ليس Boo boo(Boo((num))); ).

إذا لم يكن Boo اسمًا كتابيًا ، فلا يمكن أن يكون إعلانًا ولا تحدث مشكلة.

وأود أيضا أن أؤكد أن Boo(8); ينشئ مؤقتًا جديدًا من النوع Boo ، حتى داخل نطاق الفصل وتعريف المُنشئ. ليست ، كما قد يظن المرء خطأً ، دعوة إلى المنشئ باستخدام this المؤشر مثل المتصل لوظائف الأعضاء غير الثابتة. لا يمكن استدعاء مُنشئ آخر بهذه الطريقة داخل هيكل المُنشئ. هذا ممكن فقط في قائمة مُهيئ الأعضاء الخاصة بالمنشئ.

يحدث هذا على الرغم من سوء الإعلان بسبب عدم وجود مُنشئ ، بسبب [stmt.ambig]/3 :

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

[...]

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

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





most-vexing-parse