编译器在 const ref 类型参数上使用临时对象时是否应该警告不安全的行为?

Should compiler warn about unsafe behaviour when using temporary object on const ref type parameters?

本文关键字:警告 不安全 是否 ref const 类型参数 临时对象 编译器      更新时间:2023-10-16

让我们考虑以下不正确的代码:

#include <string>
#include <iostream>
struct C {
C(const std::string &_s): s(_s) { }
void run() {
std::cout << "Here we are using the string " << s << "n";
}
const std::string &s;
};
int main() {
C a("/tmp/example.log");
a.run();
return 0;
}

g++ 6.3.0 作为 clang 3.8.1 编译此代码时没有警告,即使使用-W -Wall -Werror,但即使对于粗心的程序员来说,这种行为也只是说这里出了点问题:

Here we are using the string Here we are usin

为什么结果不像人们天真期望的那样?从const char *创建std::string对象的生存期仅限于构造函数的范围。因此,当构造函数返回foo()时,foo::s字段具有对不存在对象的引用。卡布姆。

如果有人理解这个想法,就很容易引入修复程序。它可以是显式创建std::string对象并将其作为参数传递:

std::string s("/tmp/example.log");
C a(s);

另一个,我认为更安全一点的是将foo::s字段声明更改为:

const std::string s;

创建参数的本地不可修改副本,但它会增加复制对象的成本。好吧,在这个例子中,复制 std::string 不会自动导致进行深度复制,但对于自己实现的类来说,这可能是完全不同的事情。

对于 C++11,很少有其他方法可用,如本答案中所述,但是 - 例如 - 在嵌入式 Linux 环境中,当您坚持使用旧的编译器和一些遗留代码时,它不一定是一个相当大的选择。

但是,无论程序员选择哪种解决方案,都必须知道存在问题。即使我正确地编写了代码,我也愚蠢地认为编译器会足够聪明地检测到明显危险的情况,如果其他开发人员陷入传递const char *参数的陷阱。但是,我决定编写一些代码来检查编译器的行为,我很失望 - 很容易陷入引用已经不存在的对象的陷阱。

将临时对象作为 const 引用传递是一个公认的危险,并且在 C++11 中以某种方式解决了这个问题,但仍然将积极抵制这种行为的责任转嫁给程序员。那么,期望编译器发出警告是合理的,还是应该依赖外部静态分析工具?

通过设计将临时绑定到常量参考作品,不应发出任何警告。它不是代码中问题的根源。问题是您使用 const 引用作为成员,而不是管理它的正确性。即使您按照建议传递std::string,这也不能消除问题 - 该字符串必须比您的对象活得更久,您的程序才是正确的。并且创建成员为std::stringconst std::string并不是更安全,而是在这种情况下安全且适当的方法。在某些情况下,您可能希望将 const 或 non const 引用存储为成员,但您必须了解您正在做什么并确保引用不会悬而未决。编译器不太可能检测到极端情况并在这种情况下发出警告。

IMO 的问题不是将临时对象作为 const 引用传递,而是将其存储为常量引用。将成员更改为const std::string s;(甚至非常量)将解决此问题。

如果计划将其存储为参考,则必须确保对象生存期在外部。事实上,如果不够小心,C++很容易射到自己的腿上。

而且我真的不希望编译器在存储输入参数作为引用时发出警告,即使如果不小心可能会很危险,因为存在有效的用例(即参数生存期超过对象生存期的所有情况,我不想在内部复制它)。

请注意,这同样适用于存储为指针等。

此外,按值传递参数(与 const ref 相反)根本无法解决问题,因为这样的参数作为值复制仍然是临时的,因此当绑定到引用时,当构造函数离开时,它也会消失。