在编译时检测或避免对临时文件的无效引用

Detect or avoid dead references to temporary on compile time

以下最小程序在使用 -O3-O2 编译时会出现段错误,但使用 -O0(使用 clang 4.0)可以正常执行:

#include <iostream>

class A {
public:
  virtual void me() const { std::cerr << "hi!\n"; }
};

class B {
public:
  B(const A& a_) : a(a_) {}
  virtual void me() const { a.me(); }

private:
  const A& a;
};

class C {
public:
  C(const B& b_) : b(b_) {}
  void me() const { b.me(); }

public:
  const B& b;
};

int main() {
  C c = C(A());
  c.me();
}

原因是 c.b 是用 class B 的临时对象的引用初始化的,该临时对象是从临时 A 构造的。构造函数 c.C() 退出后,临时 B 消失了,但对它的引用仍在 c.b.

鉴于我无法控制 BA 的实施,我可以采用哪些好的做法来避免这种情况?是否有能够检测到这种情况的静态分析仪? (我的scan-build版本没有发现问题。)

相关:

我会从 BC 派生出单独的 classes(甚至可能使用模板 class)。

这些 classes 将包含一个非引用成员,它成为 ab 所引用的东西。

然后我会在这些派生的 classes 中实现必要的复制构造函数/赋值运算符,以防止悬空引用。

(然后我会与 BC 的作者进行热烈的对话)。

我个人不喜欢将成员变量作为引用。它们不能被复制或移动,并且 class 的用户必须确保对象比引用本身长。一个例外可能是内部辅助 class 设计用作临时对象,例如函子。

用指针替换引用应该很简单,然后如果你在构造函数中也使用指针:

class C {
public:
  C(const A *a_) : a(a_) {}
private:
  const A *a;
};

...等等。如果class很大,懒得改成员,可以直接改构造函数:

class C {
public:
  C(const A *a_) : a(*a_) {}
private:
  const A &a;
};

为了滥用此 class,正如 OP 所说,您必须编写如下内容:

C c = C(&A());

然后错误应该很明显:将指针指向临时对象是一个非常糟糕的主意!

PS:我会在你的构造函数中添加一个explicit,只是为了避免它参与自动转换,我认为这是你问题的一部分。