在 C++ 中检测不安全的 const 引用绑定
Detecting unsafe const reference binding in C++
我刚刚花了很多时间在我的一个程序中调试一个模糊的内存损坏问题。它本质上归结为一个函数,该函数 returns 以一种将其传递给对象构造函数的方式调用按值结构。伪代码如下。
extern SomeStructure someStructureGenerator();
class ObjectWhichUsesStructure {
ObjectWhichUsesStructure(const SomeStructure& ref): s(ref) {}
const SomeStructure& s;
}
ObjectWhichUsesStructure obj(someStructureGenerator());
我的推理是:someStructureGenerator()
正在返回一个临时的; this 被绑定到一个 const 引用,这意味着编译器正在延长临时文件的生命周期以匹配使用地点;我正在使用它来构造一个对象,因此临时生命周期被延长以匹配该对象的生命周期。
最后一点事实并非如此。一旦构造函数退出,编译器就会删除临时文件,现在 obj
包含对超空间的引用,当我尝试使用它时会出现搞笑的结果。我需要明确地将 const 引用绑定到范围,如下所示:
const auto& ref = someStructureGenerator();
ObjectWhichUsesStructure obj(ref);
我问的不是这个。
我要问的是:我的编译器是 gcc 8,我用 -Wall
构建,非常高兴 编译上面的代码 - -- 干净利落,没有任何警告。我的程序 运行 在 valgrind 下愉快地(但不正确地),同样没有警告。
我不知道我的代码中还有多少其他地方使用了相同的习惯用法。什么编译器工具会检测并标记这些地方,以便我可以修复我的代码,并在我以后犯同样的错误时提醒我?
首先,引用绑定确实在这里“延长了生命周期”——但只是延长了构造函数参数的生命周期(不再是无论如何比临时物化的那个)。 s(ref)
没有绑定一个 对象 (因为 ref
已经是一个引用),所以不会发生进一步的扩展。
因此可以通过聚合初始化执行您期望的扩展:
struct ObjectWhichUsesStructure {
const SomeStructure &s;
};
ObjectWhichUsesStructure obj{someStructureGenerator()};
这里没有构造函数参数(因为根本没有构造函数!)所以只有一个,需要的绑定发生。
值得一看的是编译器为何不对此发出警告:即使构造函数确实保留了对临时参数的引用,也有合法情况 有效:
void useWrapper(ObjectWhichUsesStructure);
void f() {useWrapper(someStructureGenerator());}
此处 SomeStructure
一直存在到语句结束,在此期间 useWrapper
可以利用 ObjectWhichUsesStructure
.
中的引用获利
以禁止上述有效用例为代价,您可以通过提供已删除的构造函数让编译器捕获有问题的案例采用右值参考:
struct ObjectWhichUsesStructure {
ObjectWhichUsesStructure(const SomeStructure& ref): s(ref) {}
ObjectWhichUsesStructure(SomeStructure&&)=delete;
const SomeStructure& s;
};
这可能值得暂时作为一种诊断措施进行,而不是永久限制。
我刚刚花了很多时间在我的一个程序中调试一个模糊的内存损坏问题。它本质上归结为一个函数,该函数 returns 以一种将其传递给对象构造函数的方式调用按值结构。伪代码如下。
extern SomeStructure someStructureGenerator();
class ObjectWhichUsesStructure {
ObjectWhichUsesStructure(const SomeStructure& ref): s(ref) {}
const SomeStructure& s;
}
ObjectWhichUsesStructure obj(someStructureGenerator());
我的推理是:someStructureGenerator()
正在返回一个临时的; this 被绑定到一个 const 引用,这意味着编译器正在延长临时文件的生命周期以匹配使用地点;我正在使用它来构造一个对象,因此临时生命周期被延长以匹配该对象的生命周期。
最后一点事实并非如此。一旦构造函数退出,编译器就会删除临时文件,现在 obj
包含对超空间的引用,当我尝试使用它时会出现搞笑的结果。我需要明确地将 const 引用绑定到范围,如下所示:
const auto& ref = someStructureGenerator();
ObjectWhichUsesStructure obj(ref);
我问的不是这个。
我要问的是:我的编译器是 gcc 8,我用 -Wall
构建,非常高兴 编译上面的代码 - -- 干净利落,没有任何警告。我的程序 运行 在 valgrind 下愉快地(但不正确地),同样没有警告。
我不知道我的代码中还有多少其他地方使用了相同的习惯用法。什么编译器工具会检测并标记这些地方,以便我可以修复我的代码,并在我以后犯同样的错误时提醒我?
首先,引用绑定确实在这里“延长了生命周期”——但只是延长了构造函数参数的生命周期(不再是无论如何比临时物化的那个)。 s(ref)
没有绑定一个 对象 (因为 ref
已经是一个引用),所以不会发生进一步的扩展。
因此可以通过聚合初始化执行您期望的扩展:
struct ObjectWhichUsesStructure {
const SomeStructure &s;
};
ObjectWhichUsesStructure obj{someStructureGenerator()};
这里没有构造函数参数(因为根本没有构造函数!)所以只有一个,需要的绑定发生。
值得一看的是编译器为何不对此发出警告:即使构造函数确实保留了对临时参数的引用,也有合法情况 有效:
void useWrapper(ObjectWhichUsesStructure);
void f() {useWrapper(someStructureGenerator());}
此处 SomeStructure
一直存在到语句结束,在此期间 useWrapper
可以利用 ObjectWhichUsesStructure
.
以禁止上述有效用例为代价,您可以通过提供已删除的构造函数让编译器捕获有问题的案例采用右值参考:
struct ObjectWhichUsesStructure {
ObjectWhichUsesStructure(const SomeStructure& ref): s(ref) {}
ObjectWhichUsesStructure(SomeStructure&&)=delete;
const SomeStructure& s;
};
这可能值得暂时作为一种诊断措施进行,而不是永久限制。