12-30-2021, 09:30 PM
代码:
class A
{
int *p_int;
public:
A(int *p): p_int(p){}
const A &add() const { (*p_int)++; return *this; }
int val() const { return *p_int; }
};
inline A construct(int *x)
{
return A(x);
}
int main()
{
int v = 42;
#ifdef ADD
// ASAN will complain
auto &obj = construct(&v).add();
#else
// ASAN does not complain
const auto &obj = construct(&v);
obj.add();
#endif
return obj.val();
}
这段代码中 #ifdef 到 #endif 之间的两段代码区别是什么?
开始我没想明白,到 #gcc 问了一下,得到的答案也看不懂,于是我尝试着自己看 Clang AST 了解这两段代码的语义。搞明白下面这几点,就知道答案了。
1. clang++ -Xclang -ast-dump -fsyntax-only test.cc 可以查看 test.cc 的 AST. 里面有一个节点叫 MaterializeTemporaryExpr.
2. 看 Clang 的源码,clang/include/clang/AST/ExprCXX.h 有 MaterializeTemporaryExpr 注释中写着引用绑定可以延长临时值的生命周期。
里面举了个例子: ``const int &r = 1.0;`` 这个语句中,将浮点数 1.0 转为整数产生了一个临时值,通过引用绑定后,这个临时值的生命周期得到延长,不会被丢弃。
于是上面第一段代码出问题的原因,实际上是 ``construct(&v)`` 在语句执行结束之后就被丢弃,临时对象被析构,导致后续尝试使用时出错。