我们都知道这样的事情在c ++中是有效的:
const T &x = T();
,同时:
T &x = T();
不是。
在a recent question中,对话会导致此规则。 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
版本感到幸运,因为编译了所有const
并直接打印出10
?我错过了规则的哪个方面?
答案 0 :(得分:15)
它只是表明通过“在编译器中尝试”来分析语言行为通常不会产生任何有用的结果。由于同样的原因,你的两个例子都是无效的。
临时的生命周期仅在您使用临时值作为const引用的直接初始化程序时才会扩展 - 只会在引用和临时引用之间建立“生命周期”链接。
尝试传递一个临时的构造函数的参数并在构造函数中附加一个const引用将不会建立上述链接,也不会延长临时的生命周期。
另外,按照C ++标准,如果你这样做
struct S {
const int &r;
S() : r(5) {
cout << r; // OK
}
};
临时的生命周期仅延伸到构造函数的末尾。一旦构造函数完成,临时就会死掉,意味着这个
S s;
cout << s.r; // Invalid
无效。
int
的实验只是“似乎有效”,纯粹是偶然的。
答案 1 :(得分:3)
你很幸运。将B :: b更改为:
void b() {
int i = rand();
int j = rand();
cout << _a << endl;
}
打印出随机数。
答案 2 :(得分:3)
每次打印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))
之后,临时文件将被销毁。