سؤال

نعلم جميعًا أن أشياء مثل هذه صالحة في C ++:

const T &x = T();

في حين:

T &x = T();

ليس.

في سؤال حديث المحادثة تؤدي إلى هذه القاعدة. قام OP بنشر بعض التعليمات البرمجية التي تثير بوضوح UB. لكنني كنت أتوقع أن تعمل نسخة معدلة منه (هذا هو الإصدار المعدل):

#include <iostream>
using namespace std;

class A {
public:
    A(int k) { _k = k; };
    int get() const { return _k; };
    int _k;
};

class B {
public:
    B(const A& a) : _a(a) {}
    void b() { cout << _a.get(); }
    const A& _a;
};

B* f() {
    return new B(A(10));
}

int main() {
    f()->b();
}

هذا يطبع القمامة على بعض الآلات ، 10 على الآخرين ... يبدو مثل UB لي :-). ولكن بعد ذلك فكرت ، حسنا A هو في الأساس تمجيد int كل ما يفعله تهيئة واحدة وقراءته. لماذا لا تتصل فقط A و int وانظر ما يحدث:

#include <iostream>
using namespace std;

typedef int A;

class B {
public:
    B(const A& a) : _a(a) {}
    void b() { cout << _a; }
    const A& _a;
};

B* f() {
    return new B(A(10));
}

int main() {
    f()->b();
}

يطبع 10 كل مره. على الأقل يبدو مثل القاعدة المرجعية const هي سارية ل int الإصدار ، ولكن ليس لإصدار الفصل. هل كلاهما ببساطة UB بسبب استخدام الكومة؟ هل أنا محظوظ مع int الإصدار لأن التجميع رأى من خلال كل شيء constS وطبعت مباشرة أ 10؟ أي جانب من جوانب القاعدة أفتقد؟

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

المحلول

إنه يوضح ببساطة أن تحليل سلوك اللغة من خلال "تجربته في المترجم" لا ينتج عادة أي نتائج مفيدة. كلا الأمثلة الخاصة بك غير صالحة لنفس السبب.

يتم تمديد عمر المؤقتة فقط عند استخدام هذا المؤقت كإيصال المباشر لمرجع const - فقط الذي سيؤسس رابط "مدى الحياة" بين المرجع والمؤقت.

إن محاولة تمرير مؤقت كحجة لمؤسسة وربط مرجع const داخل المنشئ لن ينشئ الرابط المذكور أعلاه ولن يمتد عمر المؤقتة.

أيضًا ، وفقًا لمعايير C ++ ، إذا قمت بذلك

struct S {
  const int &r;

  S() : r(5) {
    cout << r; // OK
  }
};

يمتد عمر المؤقتة فقط إلى نهاية المنشئ. بمجرد الانتهاء من المُنشئ ، يموت المؤقتة ، وهذا يعني أن هذا

S s;
cout << s.r; // Invalid

غير صالح.

تجربتك مع int ببساطة "يبدو أن العمل" ، بحتة عن طريق الصدفة.

نصائح أخرى

لقد حظيت للتو. تغيير ب :: ب إلى هذا:

void b() {
    int i = rand();
    int j = rand();
    cout << _a << endl;
}

يطبع أرقام عشوائية.

يطبع 10 في كل مرة.

تعديل الوظيفة الرئيسية قليلاً ولن تطبع 10 بعد الآن:

int main()
{
    B* p = f();
    cout << "C++\n";   // prints C++
    p->b();            // prints 4077568
}

كيف يتم إنشاء هذا الرابط في أي مستوى؟

انظر 12.2 [class.temporary] §4 و §5:

يتم تدمير الأشياء المؤقتة كخطوة أخيرة في تقييم التعبير الكامل الذي يحتوي (معجمي) على النقطة التي تم إنشاؤها فيها.

هناك سياقان يتم فيها تدمير الأعمال المعدلة في نقطة مختلفة عن نهاية التعبير الكامل. السياق الأول هو [...

السياق الثاني هو عندما يكون المرجع مرتبطًا مؤقتًا. المؤقتة التي تكون المرجع ملزمة أو المؤقتة التي هي الكائن الكامل لمشروع الفرعي الذي يلزم المرجع إليه يستمر مدى عمر المرجع باستثناء: [...

يستمر الحد المؤقت لمعلمة مرجعية في استدعاء دالة حتى الانتهاء من التعبير الكامل الذي يحتوي على المكالمة.

لذلك في حالتك ، يتم تدمير المؤقتة بعد تقييم التعبير الكامل new B(A(10)).

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