سؤال

ثابت عضو التهيئة يمكنني استخدام متداخلة مساعد البنية ، الذي يعمل بشكل جيد لعدم قالب الطبقات.ومع ذلك ، إذا كان أرفق فئة معلمات قالب المتداخلة التهيئة فئة ليست مثيل ، إذا كان كائن مساعد ليس الوصول إليها في رمز الرئيسي.للتوضيح, مثال مبسط (في حالتي أنا بحاجة إلى تهيئة متجه).

#include <string>
#include <iostream>

struct A
{
    struct InitHelper
    {
        InitHelper()
        {
            A::mA = "Hello, I'm A.";
        }
    };
    static std::string mA;
    static InitHelper mInit;

    static const std::string& getA(){ return mA; }
};
std::string A::mA;
A::InitHelper A::mInit;


template<class T>
struct B
{
    struct InitHelper
    {
        InitHelper()
        {
            B<T>::mB = "Hello, I'm B."; // [3]
        }
    };
    static std::string mB;
    static InitHelper mInit;

    static const std::string& getB() { return mB; }
    static InitHelper& getHelper(){ return mInit; }
};
template<class T>
std::string B<T>::mB; //[4]
template<class T>
typename B<T>::InitHelper B<T>::mInit;


int main(int argc, char* argv[])
{
    std::cout << "A = " << A::getA() << std::endl;

//    std::cout << "B = " << B<int>::getB() << std::endl; // [1]
//    B<int>::getHelper();    // [2]
}

مع g++ 4.4.1:

  • [1] و [2] قائلا:

    A = Hello, I'm A.

    يعمل على النحو المنشود

  • [1] uncommented:

    A = Hello, I'm A.
    B = 

    أتوقع أن InitHelper تهيئة mB

  • [1] و [2] uncommented:
    A = Hello, I'm A.
    B = Hello, I'm B.
    يعمل على النحو المنشود
  • [1] قائلا: [2] uncommented:
    Segfault في ثابت التهيئة المرحلة [3]

وبالتالي سؤالي:هذا مترجم علة أو غير علة يجلس بين الشاشة و الكرسي ؟ و إذا كان هذا الأخير هو حالة:هل هناك حل أنيق (أيدون أن يدعو صراحة ثابت أسلوب التهيئة)?

التحديث الأول:
يبدو أن هذا السلوك المطلوب (كما هو معرف في المعيار ISO/IEC C++ 2003 القياسية ، 14.7.1):

ما لم يكن عضوا في فئة قالب أو عضو في قالب صراحة مثيل صراحة أو المتخصصة ، التخصص من الأعضاء ضمنيا مثيل عندما التخصص المشار إليها في السياق الذي يتطلب الأعضاء في تعريف الوجود ؛ ولا سيما التهيئة (و أي آثار جانبية المرتبطة) من بيانات ثابتة الأعضاء لا تحدث إلا إذا كان ثابت عضو البيانات هي نفسها المستخدمة في الطريقة التي يتطلب تعريف بيانات ثابتة الأعضاء في الوجود.

هل كانت مفيدة؟

المحلول

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


struct C { C(int n) { printf("%d\n", n); } };

template<int N>
struct A {
  static C c;
}; 

template<int N>
C A<N>::c(N); 

A<1> a; // implicit instantiation of A<1> and 2
A<2> b;

لديك تعريف بيانات ثابتة الأعضاء القالب.هذا لا ليس بعد خلق أي بيانات الأعضاء, لأن من 14.7.1:

"...ولا سيما التهيئة (و أي آثار جانبية المرتبطة) من بيانات ثابتة الأعضاء لا تحدث إلا إذا كان ثابت عضو البيانات هي نفسها المستخدمة في الطريقة التي يتطلب تعريف بيانات ثابتة الأعضاء في الوجود".

تعريف شيء (= كيان) مطلوب عندما يكون هذا الكيان هو "استخدامها" ، وفقا واحد تعريف القاعدة الذي يعرف أن كلمة (في 3.2/2).ولا سيما إذا كان كل المراجع هم من uninstantiated قوالب أعضاء قالب أو sizeof تعبيرات أو ما شابه من الأمور التي لا "استخدام" الكيان (لأنها إما لا يحتمل تقييم ، أو أنها فقط لا توجد حتى الآن وظائف/وظائف الأعضاء التي هي نفسها المستخدمة) ، مثل بيانات ثابتة الأعضاء لا مثيل.

ضمني مثيل من قبل 14.7.1/7 instantiates إعلانات ثابتة أعضاء البيانات - وهذا هو القول ، فإنه سيتم إنشاء أي قالب بحاجة إلى عملية الإعلان.لن بيد مثيل التعاريف - وهذا هو القول ، المهيآت لا مثيل والمنشئات من نوع أن بيانات ثابتة الأعضاء لا ضمنا تعريف (وضع علامة المستخدمة).

أن جميع الوسائل البرمجية أعلاه سيتم إخراج شيء حتى الآن.دعونا السبب الضمني التجسيدات من أعضاء بيانات ثابتة الآن.

int main() { 
  A<1>::c; // reference them
  A<2>::c; 
}

وهذا سوف يسبب اثنين من أعضاء بيانات ثابتة موجودة ، لكن السؤال هو كيف يتم ترتيب التهيئة?في قراءة بسيطة ، قد يعتقد المرء أن 3.6.2/1 وينطبق الذي يقول (التشديد مني):

"الأجسام الساكنة تخزين المدة المحددة في مساحة اسم النطاق في نفس وحدة الترجمة و حيوي تهيئة يجب تهيئة في الترتيب الذي تعريفها يظهر في وحدة الترجمة."

الآن كما قال في آخر من الأعضاء و أوضح في تقرير عيب, هذه بيانات ثابتة أعضاء غير محددة في وحدة الترجمة ، ولكن يتم إنشاء مثيل في مثيل وحدة, ، كما هو موضح في 2.1/1:

ترجمة كل وحدة الترجمة يتم فحص لإنتاج قائمة المطلوبة التجسيدات.[ملاحظة:قد تتضمن هذه التجسيدات التي تم طلبها بشكل صريح (14.7.2).] التعاريف المطلوبة قوالب تقع.فمن التنفيذ-تحديد ما إذا كان مصدر الترجمة الوحدات التي تحتوي على هذه التعاريف هو مطلوب أن تكون متاحة.[ملاحظة:التنفيذ يمكن ترميز معلومات كافية في ترجمة وحدة الترجمة وذلك لضمان مصدر غير مطلوب هنا.] جميع التجسيدات التي تنتج مثيل الوحدات.[ملاحظة:هذه مشابهة ترجمة ترجمة الوحدات ولكن لا تحتوي على إشارات إلى uninstantiated قوالب أي قالب التعاريف.] البرنامج هو سوء شكلت إذا كان أي مثيل فشل.

نقطة مثيل من هذه الأعضاء أيضا لا يهم حقا, لأن مثل هذه نقطة مثيل هو سياق الارتباط بين مثيل لها وحدات الترجمة - أنها تحدد الإعلانات التي تظهر (على النحو المحدد في 14.6.4.1, و كل من هذه النقطة من التجسيدات يجب أن تعطي التجسيدات نفس المعنى كما هو محدد في تعريف القاعدة في 3.2/5, الرصاصة الأخيرة).

إذا كنا نريد أمر التهيئة يجب أن نرتب لذلك نحن لا تعبث مع التجسيدات ، ولكن مع صريح الإعلانات - وهذا هو مجال صريحة التخصصات ، كما أن هذه ليست حقا مختلف عن الإعلانات العادية.في الواقع, C++0x تغيير صيغة 3.6.2 إلى ما يلي:

ديناميكية التهيئة غير المحلية الكائن مع ساكنة مدة التخزين إما أمر أو غير مرتبة.تعاريف صراحة المتخصصة فئة قالب بيانات ثابتة أعضاء أمر التهيئة.أخرى قالب فئة أعضاء بيانات ثابتة (أي ضمنا أو صراحة مثيل التخصصات) يكون غير مرتبة التهيئة.


وهذا يعني أن الكود هذا:

  • [1] و [2] قائلا:أي إشارة إلى أعضاء بيانات ثابتة موجودة ، لذلك وتعاريفها (و أيضا لا إعلاناتها ، حيث أن هناك حاجة إلى إنشاء مثيل B<int>) لا مثيل.لا من الآثار الجانبية يحدث.
  • [1] uncommented: B<int>::getB() تستخدم ، وهو في حد ذاته يستخدم B<int>::mB, مما يتطلب من عضو ثابت في الوجود.سلسلة تهيئة قبل الرئيسي (في أي حال قبل هذا البيان ، كجزء من تهيئة غير المحلية الكائنات).لا يستخدم B<int>::mInit, حتى انها ليست موجودة و لذلك لا يوجد كائن من B<int>::InitHelper هل من أي وقت مضى خلق ، مما يجعل منشئ لا يتم استخدامها ، والتي بدورها سوف لا تعيين شيء B<int>::mB:سوف مجرد إخراج سلسلة فارغة.
  • [1] و [2] uncommented:أن هذا عملت لك الحظ (أو العكس :)).لا يشترط ترتيب معين من التهيئة المكالمات, كما هو موضح أعلاه.قد تعمل على VC++, تفشل في دول مجلس التعاون الخليجي والعمل على رنة.نحن لا نعرف.
  • [1] وعلق ، [2] uncommented:نفس المشكلة مرة أخرى ، سواء بيانات ثابتة الأعضاء تستخدم: B<int>::mInit يستخدم من قبل B<int>::getHelper, و إنشاء مثيل B<int>::mInit سوف يسبب لها منشئ إنشاء مثيل ، والتي سوف تستخدم B<int>::mB - ولكن بالنسبة مترجم, الأمر مختلف في هذا الخصوص تشغيل (غير محدد السلوك لا يشترط أن تكون متسقة بين أشواط مختلفة):كان تهيئة B<int>::mInit الأولى, والتي سوف تعمل على عدم بعد شيدت سلسلة كائن.

نصائح أخرى

المشكلة هي أن التعريفات التي تقدمها لمتغيرات الأعضاء الثابتة هي قوالب أيضًا.

template<class T>
std::string B<T>::mB;
template<class T>
typename B<T>::InitHelper B<T>::mInit;

أثناء التجميع، لا يحدد هذا في الواقع شيئًا، نظرًا لأن T غير معروف.إنه يشبه إعلان الفصل أو تعريف القالب، ولا يقوم المترجم بإنشاء كود أو تخزين احتياطي عندما يراه.

يحدث التعريف ضمنيًا لاحقًا، عند استخدام فئة القالب.لأنه في حالة segfaulting لا تستخدم B<int>::mInit، فلن يتم إنشاؤه أبدًا.

يتمثل الحل في تحديد العضو المطلوب بشكل صريح (دون تهيئته):ضع الملف المصدر في مكان ما أ

template<>
typename B<int>::InitHelper B<int>::mInit;

يعمل هذا بشكل أساسي بنفس طريقة تحديد فئة القالب بشكل صريح.

  • [1] حالة غير معلقة:هذا جيد. static InitHelper B<int>::mInit غير موجود.إذا لم يتم استخدام عضو فئة القالب (البنية) فلن يتم تجميعه.

  • [1] و [2] حالة غير معلقة:هذا جيد. B<int>::getHelper() يستخدم static InitHelper B<int>::mInit و mInit موجود.

  • [1] علق، [2] لم يعلق:كان يعمل بالنسبة لي في VS2008.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top