ссылка константы на временную причуду

Все мы знаем, что вещи как это допустимы в C++:

const T &x = T();

в то время как:

T &x = T();

не.

В недавнем вопросе разговор приводят к этому правилу. OP отправил некоторый код, который ясно вызывает UB. Но я имел бы, ожидают, что измененная версия его будет работать (Это - измененная версия):

#include 
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 
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 каждый раз. По крайней мере, кажется, что ссылочное правило константы в действительности для int версия, но не для версии класса. Они оба - просто UB из-за использования "кучи"? Я просто удачлив с int версия, потому что компиляция видела через все consts и просто непосредственно распечатанный a 10? Какой аспект правила я пропускаю?

8
задан Community 23 May 2017 в 11:54
поделиться

3 ответа

Он просто демонстрирует, что анализ поведения языка путем «проверки его в компиляторе» обычно не дает каких-либо полезных результатов. Оба ваших примера недействительны по той же причине.

Время жизни временного объекта увеличивается только в том случае, если вы используете его в качестве прямого инициализатора для ссылки на константу - только это установит связь «времени жизни» между ссылкой и временным элементом.

Попытка передать временный объект в качестве аргумента конструктора и присоединение ссылки на константу внутри конструктора не установит вышеупомянутую ссылку и не продлит время жизни временного объекта.

Кроме того, в соответствии со стандартом C ++, если вы сделаете это

struct S {
  const int &r;

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

, время жизни временного объекта продлится только до конца конструктора. Как только конструктор закончен, временный объект умирает, что означает, что this

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

недействителен.

Ваш эксперимент с int просто "кажется, работает" чисто случайно.

17
ответ дан 5 December 2019 в 07:10
поделиться

Вам просто повезло. Замена B::b на this:

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

выводит случайные числа.

3
ответ дан 5 December 2019 в 07:10
поделиться

Каждый раз печатается 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)) .

3
ответ дан 5 December 2019 в 07:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: