سؤال

عندما أقوم بتجميع الكود التالي باستخدام g++

class A {};

void foo(A&) {}

int main()
{
  foo(A());
  return 0;
}

أحصل على رسائل الخطأ التالية:

> g++ test.cpp -o test     
test.cpp: In function ‘int main()’:
test.cpp:10: error: invalid initialization of non-const reference of type ‘A&’ from a temporary of type ‘A’
test.cpp:6: error: in passing argument 1 of ‘void foo(A&)’

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

لكن انتظر!إذا قمت بإضافة عامل التحويل التالي إلى الفصل A

class A
{
public:
  operator A&() { return *this; }
};

ثم كل شيء على ما يرام!سؤالي هو ما إذا كان هذا آمنًا حتى عن بعد.ماذا يفعل بالضبط this أشر إلى متى A() تم بناؤه كقيمة مؤقتة؟

لقد أعطيتني بعض الثقة من خلال حقيقة ذلك

void foo(const A&) {}

يمكن قبول القيم المؤقتة وفقًا لـ g++ وجميع المترجمين الآخرين الذين استخدمتهم.ال const يمكن دائمًا التخلص من الكلمة الرئيسية، لذلك سأفاجأ إذا كانت هناك أي اختلافات دلالية فعلية بين a const A& المعلمة و A& معامل.لذا أعتقد أن هذه طريقة أخرى لطرح سؤالي:لماذا هو const إشارة إلى قيمة مؤقتة تعتبر آمنة من قبل المترجم في حين أن غيرconst المرجع لا؟

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

المحلول

وهذا لا يعني عنوان لا يمكن أن تؤخذ (المترجم أن يأمر دائما يشق على المكدس، وهو ما يحدث بالفعل مع المرجع إلى CONST)، انها مسألة المبرمجين القصد. مع واجهة التي يأخذ A &، فإنه يقول "سوف أقوم بتعديل ما هو موجود في هذه المعلمة حتى تتمكن من قراءتها بعد استدعاء دالة". إذا كنت تمر منه مؤقتا، ثم الشيء هو "المعدل" لا وجود لها بعد وظيفة. هذا هو (ربما) خطأ البرمجة، لذلك غير مسموح بها. على سبيل المثال، النظر فيما يلي:

void plus_one(int & x) { ++x; }

int main() {
   int x = 2;
   float f = 10.0;

   plus_one(x); plus_one(f);

   cout << x << endl << f << endl;
}

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


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

ofstream("bar.t") << "flah";

ولكن لا يمكن لمشغل << يأخذ-المرجع إلى غير CONST. خياراتك كسرها إلى سطرين، أو استدعاء أسلوب إرجاع-المرجع إلى غير CONST:

ofstream("bar.t").flush() << "flah";

نصائح أخرى

عند تعيين قيمة ص إلى مرجع CONST، ويضمن لك أن المؤقتة لن تدمر حتى يتم تدمير المرجعية. عند تعيين إلى مرجع غير CONST، لم ترد مثل هذه الضمانة.

int main()
{
   const A& a2= A(); // this is fine, and the temporary will last until the end of the current scope.
   A& a1 = A(); // You can't do this.
}

وأنت لا يمكن أن يلقي بأمان بعيدا CONST نيس شاء أم أبى، ونتوقع أشياء للعمل. هناك دلالات مختلفة على المراجع CONST وغير CONST.

خدعة قد يقع فيها بعض الناس:مترجم MSVC (مترجم Visual Studio، تم التحقق منه باستخدام Visual Studio 2008) سوف ترجمة هذا الرمز دون أي مشاكل.لقد كنا نستخدم هذا النموذج في مشروع ما للوظائف التي تتطلب عادةً وسيطة واحدة (مجموعة من البيانات لاستيعابها)، ولكننا أردنا أحيانًا البحث في المجموعة وإرجاع النتائج إلى المتصل.تم تمكين الوضع الآخر عن طريق أخذ ثلاث وسيطات --- الوسيطة الثانية هي المعلومات المطلوب البحث عنها (المرجع الافتراضي لسلسلة فارغة)، والوسيطة الثالثة كانت لبيانات الإرجاع (المرجع الافتراضي إلى القائمة الفارغة من النوع المطلوب).

نجح هذا النموذج في Visual Studio 2005 و2008، وكان علينا إعادة هيكلته بحيث يتم إنشاء القائمة وإعادتها بدلاً من أن تكون مملوكة للمتصل ومتحولة لتجميعها باستخدام g++.

إذا كانت هناك طريقة لتعيين مفاتيح التحويل البرمجي إما لعدم السماح بهذا النوع من السلوك في MSVC أو السماح به في g++، سأكون متحمسًا لمعرفة ذلك؛إن سماحية مترجم MSVC / تقييد مترجم g ++ يضيف تعقيدات إلى كود النقل.

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