Все мы знаем, что вещи как это допустимы в 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
версия, потому что компиляция видела через все const
s и просто непосредственно распечатанный a 10
? Какой аспект правила я пропускаю?
Он просто демонстрирует, что анализ поведения языка путем «проверки его в компиляторе» обычно не дает каких-либо полезных результатов. Оба ваших примера недействительны по той же причине.
Время жизни временного объекта увеличивается только в том случае, если вы используете его в качестве прямого инициализатора для ссылки на константу - только это установит связь «времени жизни» между ссылкой и временным элементом.
Попытка передать временный объект в качестве аргумента конструктора и присоединение ссылки на константу внутри конструктора не установит вышеупомянутую ссылку и не продлит время жизни временного объекта.
Кроме того, в соответствии со стандартом C ++, если вы сделаете это
struct S {
const int &r;
S() : r(5) {
cout << r; // OK
}
};
, время жизни временного объекта продлится только до конца конструктора. Как только конструктор закончен, временный объект умирает, что означает, что this
S s;
cout << s.r; // Invalid
недействителен.
Ваш эксперимент с int
просто "кажется, работает" чисто случайно.
Вам просто повезло. Замена B::b на this:
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))
.