0

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

#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

为什么结果不像人们天真地期望的那样?std::string创建的对象的生命周期 const char *仅限于构造函数的范围。因此,当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 中以某种方式解决,但仍将责任转嫁给程序员以积极抵制这种行为。那么,期望编译器发出警告是合理的,还是应该依赖外部静态分析工具?

4

2 回答 2

3

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

于 2017-06-15T13:12:53.853 回答
0

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

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

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

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

此外,通过值传递参数(与 const ref 相对)根本不能解决问题,因为这样复制为 value 的参数仍然是临时的,所以当绑定到引用时,它也会在构造函数时消失左。

于 2017-06-15T13:15:50.593 回答