对临时异常的 const 引用
我们都知道这样的事情在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
版本,因为编译看到了所有 const
并直接打印出了 10
吗?我缺少规则的哪一方面?
We all know that things like this are valid in c++:
const T &x = T();
while:
T &x = T();
is not.
In a recent question the conversation lead to this rule. The OP had posted some code which clearly evokes UB. But I would have expect a modified version of it to work (This is the modified version):
#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();
}
This prints garbage on some machines, 10 on others... sounds like UB to me :-). But then I thought, well A
is basically a glorified int
all it does it initialize one and read it. Why not just call A
an int
and see what happens:
#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();
}
It prints 10
every time. It at least seems like the const reference rule is in effect for the int
version, but not for the class version. Are they both simply UB due to the use of the heap? Am I just lucky with the int
version because the compile saw through all const
s and just directly printed out a 10
? Which aspect of the rule am I missing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
它只是表明通过“在编译器中尝试”来分析语言行为通常不会产生任何有用的结果。由于同样的原因,您的两个示例均无效。
仅当您使用该临时文件作为 const 引用的直接初始化程序时,该临时文件的生命周期才会延长 - 只有这样才会在引用和临时文件之间建立“生命周期”链接。
尝试将临时变量作为构造函数的参数传递并在构造函数内部附加 const 引用将不会建立上述链接,也不会延长临时变量的生命周期。
另外,根据 C++ 标准,如果执行此操作,
临时变量的生命周期只会延长到构造函数的末尾。一旦构造函数完成,临时变量就会消失,这意味着这
是无效的。
您对
int
的实验只是“似乎有效”,纯粹是偶然的。It simply demonstrates that analyzing language behavior by "trying it in the compiler" doesn't normally produce any useful results. Both of your examples are invalid for the very same reason.
The lifetime of the temporary is only extended when you use that temporary as the direct initializer for a const reference - only that will establish a "lifetime" link between the reference and the temporary.
Trying to pass a temporary as a constructor's argument and attaching a const reference inside the constructor will not establish the aforementioned link and will not extend the lifetime of the temporary.
Also, in accordance with C++ standard, if you do this
the lifetime of the temporary is only extended to the end of the constructor. Once the constructor is finished, the temporary dies, meaning that this
is invalid.
Your experiment with
int
simply "appears to work", purely by accident.你只是很幸运。将 B::b 更改为:
打印出随机数。
You just got lucky. Changing B::b to this:
prints out random numbers.
把main函数稍微修改一下,就不会再打印10了:
参见 12.2 [class.temporary] §4 和 §5:
因此,在您的情况下,在评估完整表达式 new B(A(10)) 后临时对象将被销毁。
Modify the main function a little and it won't print 10 anymore:
See 12.2 [class.temporary] §4 and §5:
So in your case, the temporary is destroyed after the evaluation of the full-expression
new B(A(10))
.